最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

VUE?使用canvas繪制管線管廊示例詳解

 更新時間:2023年04月27日 11:41:19   作者:前端界的CV大師  
這篇文章主要為大家介紹了VUE?使用canvas繪制管線/管廊實現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

引言

上節(jié)說完了基本的實現(xiàn)思路,那今天我將展開來說一下具體的實現(xiàn)方法。本節(jié)主要是處理每一個繪制是如何進行的。

那就直接開始吧。

首先,我在JS中定義了一下常量,其作用如下:

let canvas = {}, ctx = {};
const image = new Image();
// 定義管線默認寬度
const PIPELINE_NUMBER = 15;
// 定義圖標默認縮放比例
const ICON_SCALE_RATIO = 25;
// 所有繪制元素
let allElementCollection = [];
// 初始化管線水類型: 0為冷水 1為熱水
let pipeline_water_type = 0;
// 當前繪制設備的對象
let equipment_select = {};
// 是否顯示設備繪制的范圍
let equipment_area_show = false
// 初始化繪制類型:0為管線 1為設備,2為文字框 默認為管線
let draw_element_type = 0;
// 管線流動速度初始值
let pipeline_offset = 0;
// 定義當前選中的已繪制元素
let current_select_element_index = {};

接下來是處理鼠標左鍵在按下后,在上節(jié)的代碼中,針對于鼠標左鍵按下后,需要判斷當前點擊的區(qū)域是否已經(jīng)存在繪制的內(nèi)容,如果有,執(zhí)行繪制內(nèi)容移動,如果沒有,則開始新建繪制內(nèi)容,代碼如下:

if (shape) {
    moveAllElement(e, clickX, clickY, rect, shape);
    canvas.style.cursor= "move";
} else {
    if (e.buttons === 1) {
        draw_element_type === 0 ? drawRealTimePipeline(e, clickX, clickY, rect) : (draw_element_type === 1 ? drawRealTimeEquipment(e, clickX, clickY, rect) : drawRealTimeText(e, clickX, clickY, rect))
    }
}

我們一個一個的來,如果繪制內(nèi)容不存在,則執(zhí)行新建繪制內(nèi)容,這里我使用三目運算來判斷當前需要繪制的類型drawRealTimePipeline、drawRealTimeEquipment、drawRealTimeText。

drawRealTimePipeline:繪制實時管線

// 繪制實時管線
const drawRealTimePipeline = (e, clickX, clickY, rect) => {
    const shape = new ElementFactory(clickX, clickY);
    shape.endX = clickX;
    shape.endY = clickY;
    // 繪制管線時,刪除通過 new 的對象的 textInfo 和 equipmentInfo,這兩個對于管線來說沒有用處
    delete shape.textInfo;
    delete shape.equipmentInfo;
    let shapeWidth = 0, shapeHeight = 0;
    // 為了方便處理元素刪除,動態(tài)添加一個隨機的 ID,并且在當前位置拿到,方便在繪制線段過短時來精確刪除
    let current_uid = setuid(shape);
    allElementCollection.push(shape);
    window.onmousemove = (evt) => {
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        shapeWidth = (evt.clientX - rect.left) - clickX;
        shapeHeight = (evt.clientY - rect.top) - clickY;
        // 判斷繪制為 豎線 還是 橫線
        let shapeDirection = Math.abs(shapeWidth) >= Math.abs(shapeHeight);
        if (shapeDirection) {
            // 如果是橫線,則 endY 為固定值
            shape.endX = evt.clientX - rect.left;
            shape.endY = clickY + PIPELINE_NUMBER;
        } else {
            // 如果是豎線,則 endX 為固定值
            shape.endX = clickX + PIPELINE_NUMBER;
            shape.endY = evt.clientY - rect.top;
        }
        shape.pipelineInfo.direction = shapeDirection;
        shape.pipelineInfo.waterType = pipeline_water_type;
        draw();
    };
    // 畫線時,鼠標抬起判斷如果線段繪制過短,則不推入 allElementCollection
    window.onmouseup = () => {
        if(parseInt(draw_element_type) === 0 && shape.endX) {
            if (Math.abs(shape.startX - shape.endX) < 45 && Math.abs(shape.startY - shape.endY) < 45) {
                let index = allElementCollection.findIndex(item => item.uid === current_uid);
                allElementCollection.splice(index, 1)
                ctx.clearRect(0, 0, canvas.width, canvas.height)
                draw()
            }
        }
    };
}
const setuid = (shape) => {
    // 生成唯一ID
    let uid = Math.round( Math.random() * 100000000000);
    shape.uid = uid;
    return uid
}

drawRealTimeEquipment:繪制實時設備:

繪制設備時,由于繪制的圖片,所以對于構造函數(shù)中的endX、endY需要自己計算

// 繪制實時設備
const drawRealTimeEquipment = (e, clickX, clickY, rect) => {
    const shape = new ElementFactory(clickX, clickY)
    // 繪制設備時,刪除通過 new 的對象的 textInfo 和 pipelineInfo,這兩個對于圖形來說沒有用處
    delete shape.textInfo;
    delete shape.pipelineInfo;
    // 設備繪制在鼠標點擊的那一刻就需要開始創(chuàng)建,
    setEquipment(e);
    setuid(shape);
    allElementCollection.push(shape);
    window.onmousemove = (evt) => setEquipment(evt);
    function setEquipment(evt) {
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        shape.startX = evt.clientX - rect.left;
        shape.startY = evt.clientY - rect.top;
        // 計算當前繪制的endX endY
        image.src = require(`../assets/images/${equipment_select.iconPath}`);
        let icon_width = Math.ceil(image.width / ICON_SCALE_RATIO),
            icon_height = Math.ceil(image.height / ICON_SCALE_RATIO);
        shape.endX = evt.clientX - rect.left + icon_width;
        shape.endY = evt.clientY - rect.top + icon_height;
        draw();
    }
    draw();
};

drawRealTimeText:繪制實時文本:

// 繪制實時文字
const drawRealTimeText = (e, clickX, clickY, rect) => {
    const shape = new ElementFactory(clickX, clickY);
    setuid(shape);
    // 繪制文字時,刪除通過 new 的對象的 equipmentInfo 和 pipelineInfo,這兩個對于圖形來說沒有用處
    delete shape.equipmentInfo;
    delete shape.pipelineInfo;
    ctx.font = `normal normal normal ${shape.textInfo.fontSize + 'px' || '16px'} Microsoft YaHei`;
    const defaultText = '默認文字,請右鍵修改';
    const measureText = ctx.measureText(defaultText);
    const textW = measureText.width,
        textH = measureText.actualBoundingBoxAscent + measureText.actualBoundingBoxDescent;
    shape.textInfo.text = defaultText;
    allElementCollection.push(shape);
    setText(e)
    window.onmousemove = (evt) => setText(evt)
    function setText(evt) {
        ctx.clearRect(0, 0, canvas.width, canvas.height)
        shape.startX = evt.clientX - rect.left;
        shape.startY = evt.clientY - rect.top;
        shape.endX = evt.clientX - rect.left + textW;
        shape.endY = evt.clientY - rect.top - textH;
        draw();
    }
    draw();
};

接下來是鼠標點的位置,已經(jīng)存在了繪制的內(nèi)容,那么這個時候就有兩種情況了,一個是 鼠標左鍵 點擊的,一個是 鼠標右鍵 點擊的。

鼠標左鍵點擊只能執(zhí)行移動,鼠標右鍵則是彈出操作框,如圖:

這里有一點需要注意,管線的操作框中沒有 編輯 按鈕,且彈出框的位置需要根據(jù)鼠標點擊的位置變化而變化

moveAllElement:元素移動事件:

// 元素移動
const moveAllElement = (e, clickX, clickY, rect, shape) => {
    const { startX, startY, endX, endY } = shape;
    let tipX = 0, tipY = 0;
    // 鼠標左鍵:拖動位置
    if (e.buttons === 1) {
        window.onmousemove = (evt) => {
            removeEditTip();
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            const distanceX = evt.clientX - rect.left - clickX;
            const distanceY = evt.clientY - rect.top - clickY;
            shape.startX = startX + distanceX;
            shape.startY = startY + distanceY;
            shape.endX = endX + distanceX;
            shape.endY = endY + distanceY;
            draw();
        }
    }
    // 鼠標右鍵:執(zhí)行信息編輯
    if (e.buttons === 2) {
        if (shape.type === 0) {
            // 管線
            tipX = e.clientX;
            tipY = e.clientY + 10;
        } else if (shape.type === 1) {
            // 如果點擊的是圖標,彈出提示出現(xiàn)在圖標下方
            tipX = (shape.endX - shape.startX) / 2 + shape.startX + rect.left
            tipY = shape.endY + rect.top
        } else if (shape.type === 2) {
            // 文字
            tipX = shape.startX + rect.left + ctx.measureText(`${shape.textInfo.text}`).width / 2;
            tipY = shape.startY + rect.top;
        }
        createEditTip(tipX, tipY, shape);
        return false
    }
};

createEditTip為動態(tài)創(chuàng)建的DOM結構,即操作提示框。

createEditTip、removeEditTip:動態(tài)創(chuàng)建及移除DOM操作提示框:

// 創(chuàng)建管線點擊事件彈窗
const createEditTip = (x, y, shape) => {
    let width = shape.type ? 180 : 120, marginLeft = shape.type ? 95 : 65, display = shape.type ? 'inline-block' : 'none'
    removeEditTip()
    let tipText = document.createElement('div')
    tipText.classList.add('tip-text-content')
    tipText.innerHTML = `<div class="tip-text" id="tipText" style="top: ${y + 10}px;left: ${x}px; width: ${width}px; margin-left:-${marginLeft}px; ">
                            <p>
                                <span id="equipmentDelete">刪除</span>
                                <span id="${parseInt(shape.type) === 2 ? 'textModify' : 'equipmentModify'}" style="display: ${display}">編輯</span>
                                <span id="buttonCancel">取消</span>
                            </p>
                         </div>`
    document.body.appendChild(tipText)
    document.getElementById('equipmentDelete').onclick = () => {
        allElementCollection.splice(current_select_element_index, 1)
        ctx.clearRect(0, 0, canvas.width, canvas.height)
        draw()
        removeEditTip()
    };
    // 判斷點擊的是 圖片 的編輯按鈕,還是 文字 的編輯按鈕
    let modifyButton = document.getElementById('equipmentModify') ? 'equipmentModify' : 'textModify';
    document.getElementById(modifyButton).onclick = () => {
        removeEditTip()
    };
    document.getElementById('buttonCancel').onclick = () => {
        removeEditTip()
    };
};
// 移除管線事件彈窗
const removeEditTip = () => {
    const popup = document.querySelector('.tip-text-content')
    if (popup) document.body.removeChild(popup)
}

寫在最后,必看

本節(jié)主要介紹看了如何動態(tài)的去 創(chuàng)建繪制 內(nèi)容的對象,所有實現(xiàn)繪制效果的方法均在 每個 new出來的shape 對象中,我們只需要循環(huán)調(diào)用每個對象的 繪制方法 即可,在本節(jié)中未涉及。

本節(jié)先到此為止,下節(jié)我會將 構造函數(shù) 中的各種繪制方法進行完善,并且會詳細的說一下如何將 allElementCollection 中的對象來繪制到canvas界面中。

以上就是VUE 使用canvas繪制管線/管廊的詳細內(nèi)容,更多關于VUE canvas繪制管線/管廊的資料請關注腳本之家其它相關文章!

相關文章

  • 淺談vue加載優(yōu)化策略

    淺談vue加載優(yōu)化策略

    這篇文章主要介紹了淺談vue加載優(yōu)化策略,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2019-03-03
  • vue實現(xiàn)h5掃碼的代碼示例

    vue實現(xiàn)h5掃碼的代碼示例

    html5-qrcode是一個基于JavaScript?輕量級和跨平臺的掃碼插件,允許用戶使用攝像頭掃描二維碼,并且解析為文本或者url,本文給大家介紹了vue實現(xiàn)h5掃碼,需要的朋友可以參考下
    2024-01-01
  • Vue.js 表單控件操作小結

    Vue.js 表單控件操作小結

    這篇文章給大家介紹了Vue.js 表單控件操作的相關知識,本文通過實例演示了input和textarea元素中使用v-model的方法,本文給大家介紹的非常不錯,具有參考借鑒價值,需要的朋友參考下吧
    2018-03-03
  • Vue自定義CSS變量的使用方法

    Vue自定義CSS變量的使用方法

    隨著前端技術的發(fā)展,CSS?變量(也稱為?CSS?定制屬性)成為了現(xiàn)代?Web?開發(fā)中不可或缺的一部分,在?Vue.js?應用程序中,使用?CSS?變量不僅可以增強樣式的靈活性,還能提高開發(fā)效率,本文將詳細介紹如何在?Vue?項目中引入并使用?CSS?變量,需要的朋友可以參考下
    2024-09-09
  • vxe-table?實現(xiàn)表格數(shù)據(jù)分組功能(按指定字段數(shù)據(jù)分組)

    vxe-table?實現(xiàn)表格數(shù)據(jù)分組功能(按指定字段數(shù)據(jù)分組)

    文章介紹了如何使用樹結構實現(xiàn)表格數(shù)據(jù)分組,并提供了官方文檔的鏈接,本文結合實例代碼給大家介紹的非常詳細,感興趣的朋友跟隨小編一起看看吧
    2024-11-11
  • ant-design-vue Table pagination分頁實現(xiàn)全過程

    ant-design-vue Table pagination分頁實現(xiàn)全過程

    這篇文章主要介紹了ant-design-vue Table pagination分頁實現(xiàn)全過程,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-04-04
  • Vue中Axios中取消請求及阻止重復請求的方法

    Vue中Axios中取消請求及阻止重復請求的方法

    為了防止用戶在網(wǎng)絡不好或者其他情況下短時間內(nèi)重復進行接口請求,重復發(fā)送多次請求,本文主要介紹了Vue中Axios中取消請求及阻止重復請求的方法,感興趣的可以了解一下
    2022-02-02
  • Vue3?Hooks?模塊化抽離示例詳解

    Vue3?Hooks?模塊化抽離示例詳解

    這篇文章主要為大家介紹了Vue3?Hooks?模塊化抽離示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-09-09
  • element-plus 在vue3 中不生效的原因解決方法(element-plus引入)

    element-plus 在vue3 中不生效的原因解決方法(element-plus引入)

    這篇文章主要介紹了element-plus 在vue3 中不生效的原因解決方法(element-plus引入),本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-08-08
  • vant的picker組件設置文字超長滾動方式

    vant的picker組件設置文字超長滾動方式

    這篇文章主要介紹了vant的picker組件設置文字超長滾動方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-12-12

最新評論

巴彦淖尔市| 石柱| 阿拉善盟| 龙岩市| 桑植县| 门源| 周宁县| 淳化县| 夏河县| 秦安县| 石泉县| 如东县| 定结县| 新野县| 白玉县| 肇州县| 华宁县| 鲜城| 山西省| 大余县| 天等县| 潞西市| 佳木斯市| 沽源县| 尉氏县| 龙州县| 云浮市| 隆回县| 临沧市| 天津市| 工布江达县| 东安县| 鄂尔多斯市| 凤凰县| 叶城县| 德格县| 榆树市| 连江县| 调兵山市| 上犹县| 唐海县|