VUE:使用canvas绘制管线/管廊(五)【完结】

  • Post author:
  • Post category:vue


书接上回,针对构造函数内部的绘制方法,基本上介绍完毕,本章最后介绍一下vue页面与canvas的数据传输方式以及管线信息修改的方式。

主要还是两部分,vue界面以及js。



VUE界面及样式

<!--
  文件描述:
  创建时间:2023/4/12 10:33
  创建人:ChenJinZhu
-->
<template>
    <div class="canvas-container">
        <div class="canvas-icon-content">
            <ul>
                <li v-for="(eq, index) in equipment" :key="index" @click="equipmentChange(eq, index)"
                    :class="{ 'canvas-icon-active': equipmentSelect === index }">
                    <img :src="eq.icon" :alt="eq.name">
                    <p>{{eq.name}}</p>
                </li>
            </ul>
        </div>
        <div class="canvas-content">
            <div class="canvas" id="canvas">
                <canvas id="myCanvas" ref="myCanvas"></canvas>
            </div>
            <div class="canvas-options">
                <div class="option-change-water-type">
                    <el-select v-model="value" placeholder="请选择" size="medium" @change="pipelineTypeChange">
                        <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value">
                        </el-option>
                    </el-select>
                </div>
                <el-divider direction="vertical"></el-divider>
                <div class="option-draw-text">
                    <el-button :type="addTextStatus ? 'success' : 'warning'" @click="addText">自定义文字</el-button>
                </div>
                <el-divider direction="vertical"></el-divider>
                <div class="option-commit">
                    <el-button type="primary" @click="commit">上传所绘制内容</el-button>
                </div>
                <el-divider direction="vertical"></el-divider>
                <div class="option-drag">
                    <el-button type="info" plain @click="showEquipmentArea">显示设备拖动区域</el-button>
                </div>
                <el-divider direction="vertical"></el-divider>
                <div class="option-clear">
                    <el-button type="info" @click="clearAll">清除所有绘制内容</el-button>
                </div>
            </div>
        </div>
        <div class="pipeline-info-add">
            <el-dialog :title="!modifyType ? '设备信息' : '文本信息'" :visible.sync="iconDialog" width="750px">
                <div class="icon-form-container">
                    <el-form v-if="!modifyType" :model="iconForm" label-position="right" label-width="120px"
                             :inline="true">
                        <el-form-item label="设备名称">
                            <el-input v-model="iconForm.name"></el-input>
                        </el-form-item>
                        <el-form-item label="设备编号">
                            <el-input v-model="iconForm.id"></el-input>
                        </el-form-item>
                        <el-form-item label="设备单位">
                            <el-input v-model="iconForm.unit"></el-input>
                        </el-form-item>
                        <el-form-item label="图标缩放比例">
                            <el-input-number v-model="iconForm.scale" :min="10" :max="30" label="值越大,图标越小:10-30"></el-input-number>
                        </el-form-item>
                        <el-form-item label="设备旋转角度">
                            <el-select v-model="iconForm.rotate" placeholder="请选择设备旋转角度">
                                <el-option v-for="(item, index) in rotateOptions" :key="index + 'op'" :label="item.label" :value="item.value">
                                </el-option>
                            </el-select>
                        </el-form-item>
                        <el-form-item label="是否展示信息">
                            <el-radio-group v-model="iconForm.show" size="small">
                                <el-radio-button :label="true">展示</el-radio-button>
                                <el-radio-button :label="false">隐藏</el-radio-button>
                            </el-radio-group>
                        </el-form-item>

                        <el-form-item label="描述信息">
                            <div v-for="(item, index) in iconForm.others" :key="index">
                                <el-input class="describe" v-model="iconForm.others[index]">
                                    <template slot="append">
                                        <el-button icon="el-icon-delete" @click="delDescribe(index)"></el-button>
                                    </template>
                                </el-input>
                            </div>
                            <el-button size="mini" type="primary" @click="addDescribe">新增描述</el-button>
                        </el-form-item>
                    </el-form>
                    <el-form v-else :model="textForm" label-position="right" label-width="100px" :inline="true">
                        <el-form-item label="文本内容">
                            <el-input v-model="textForm.text"></el-input>
                        </el-form-item>
                        <el-form-item label="字体大小">
                            <el-input-number v-model="textForm.fontSize" :min="16" :max="30"></el-input-number>
                        </el-form-item>
                        <el-form-item label="字体颜色">
                            <el-input disabled placeholder="请选择颜色" :value="textForm.color"
                                      @click.native="colorStatus = !colorStatus"></el-input>
                            <div class="sketch-picker" v-show="colorStatus">
                                <sketch-picker v-model="color" @input="colorValueChange"></sketch-picker>
                                <el-button size="mini" @click="colorStatus = false">取消</el-button>
                                <el-button size="mini" type="primary" @click="colorStatus = false">确定</el-button>
                            </div>
                        </el-form-item>
                    </el-form>
                    <div style="width: 100%;display: flex;justify-content: space-around;">
                        <el-button @click="iconDialog = false">取消</el-button>
                        <el-button type="primary" @click="iconModify">确定</el-button>
                    </div>
                </div>
            </el-dialog>
        </div>
    </div>
</template>

<script>
    import canvas from '../utils/canvas'

    import { Sketch } from 'vue-color'
    let myCanvas = {}
    export default {
        name: "draw",
        components: { 'sketch-picker': Sketch },
        data() {
            return {
                // 设备列表
                equipment: [
                    {
                        name: '管线',
                        eId: 11,
                        icon: require('../assets/images/line.png'),
                        iconPath: 'line.png',
                        drawType: 0
                    },
                    {
                        name: '温度表',
                        eId: 1,
                        icon: require('../assets/images/thermometer.png'),
                        iconPath: 'thermometer.png',
                        drawType: 1
                    },
                    {
                        name: '压力表',
                        eId: 2,
                        icon: require('../assets/images/manometer.png'),
                        iconPath: 'manometer.png',
                        drawType: 1
                    },
                    {
                        name: '接头',
                        eId: 3,
                        icon: require('../assets/images/joint.png'),
                        iconPath: 'joint.png',
                        drawType: 1
                    },
                    {
                        name: '直流阀',
                        eId: 4,
                        icon: require('../assets/images/direct_current_valve.png'),
                        iconPath: 'direct_current_valve.png',
                        drawType: 1
                    },
                    {
                        name: '循环阀',
                        eId: 5,
                        icon: require('../assets/images/circulating_valve.png'),
                        iconPath: 'circulating_valve.png',
                        drawType: 1
                    },
                    {
                        name: '电动阀',
                        eId: 6,
                        icon: require('../assets/images/electric_valve.png'),
                        iconPath: 'electric_valve.png',
                        drawType: 1
                    },
                    {
                        name: '开关阀',
                        eId: 7,
                        icon: require('../assets/images/on_off_valve.png'),
                        iconPath: 'on_off_valve.png',
                        drawType: 1
                    },
                    {
                        name: '热换机',
                        eId: 8,
                        icon: require('../assets/images/heat_exchanger.png'),
                        iconPath: 'heat_exchanger.png',
                        drawType: 1
                    },
                    {
                        name: '热换机',
                        eId: 9,
                        icon: require('../assets/images/heat_exchanger1.png'),
                        iconPath: 'heat_exchanger1.png',
                        drawType: 1
                    },
                    {
                        name: '蓄水池',
                        eId: 10,
                        icon: require('../assets/images/reservoir.png'),
                        iconPath: 'reservoir.png',
                        drawType: 1
                    },
                ],
                
                // 设备选中index
                equipmentSelect: 0,

                // 冷热水下拉
                options: [
                    {
                        value: '0',
                        label: '冷水'
                    }, {
                        value: '1',
                        label: '热水'
                    }
                ],
                
                 // 冷热水选中值
                value: '0',

                 // 文字添加状态
                addTextStatus: false,

                 // 修改类型:0设备信息修改、1文字信息修改
                modifyType: 0,

                // 弹窗显隐
                iconDialog: false,
                
                // 设备信息
                iconForm: {
                    show: true,

                },
                
                // 文本信息
                textForm: {},
                
                // 旋转度数
                rotateOptions: [
                    {
                        label: '0 度',
                        value: 0
                    }, {
                        label: '90 度',
                        value: 90,
                    }, {
                        label: '-90 度',
                        value: -90,
                    }, {
                        label: '180 度',
                        value: 180,
                    }
                ],
                
                // 当前选择颜色
                color: '',
                colorStatus: false,
                
                //储存临时回显数据
                echoDataList: []
            }
        },
        methods: {
            equipmentChange(element,index) {
                this.equipmentSelect = index
                this.addTextStatus = false
                canvas.drawTypeChange(element)
            },

            // 管线类型选择
            pipelineTypeChange() {
                canvas.changePipelineType(this.value)
            },

            // 添加自定义文字
            addText() {
                this.addTextStatus = !this.addTextStatus
                this.equipmentSelect = this.addTextStatus ? -1 : 0;
                canvas.drawTypeChange(this.addTextStatus ? {drawType: 2} : this.equipment[0])
            },

            // 显示设备可拖动区域
            showEquipmentArea() {
                canvas.showEquipmentIconArea()
            },

            // 监听tip的点击事件
            handleMouseup(idx) {
                this.modifyType = idx
                let currentEditObject = JSON.parse(JSON.stringify(canvas.canvasMouseUp()));

                this[['iconForm', 'textForm'][idx]] = currentEditObject[['equipmentInfo', 'textInfo'][idx]]
                this.iconDialog = true
            },
            addDescribe() {
                this.iconForm.others.push('')
            },

            // 颜色值改变事件处理
            colorValueChange(val) {
                this.textForm.color = val.hex
            },

            // 设备信息修改
            iconModify() {
                // console.log(this[['iconForm', 'textForm'][this.modifyType]])
                canvas.canvasModifyInfo(this[['iconForm', 'textForm'][this.modifyType]], ['equipmentInfo', 'textInfo'][this.modifyType])
                this.iconDialog = false
            },

            //删除描述信息
            delDescribe(index) {
                this.iconForm.others.splice(index, 1)
            },

            commit() {
                canvas.commit()
            },
            clearAll() {
                canvas.clearAll()
            }
        },
        mounted() {
            myCanvas = canvas.init('myCanvas')
            window.addEventListener('click', (e) => {
                if (e.target.id.includes('equipmentModify')) this.handleMouseup(0)
                if (e.target.id.includes('textModify')) this.handleMouseup(1)
            })
        },
        created() {
        },
        beforeDestroy() {
            window.removeEventListener('click')
        }
    }
</script>

<style scoped lang="less">

   .canvas-container{
       width: 1300px;
       border: 1px solid #000000;
       border-radius: 20px;
       margin: 10px;
       overflow: hidden;
       display: flex;
       *{
           box-sizing: border-box;
       }
       .canvas-icon-content{
           width: 100px;
           border-right: 1px solid #000;
           cursor: pointer;
           overflow: hidden;
           ul{
               height: 100%;
               display: flex;
               flex-direction: column;
               justify-content: space-between;
               align-items: center;
               li{
                   padding: 5px 0;
                   img{
                       display: block;
                       max-width: 35%;
                       margin: 0 auto;
                   }
                   p{
                       text-align: center;
                       font-size: 12px;
                       letter-spacing: 3px;
                   }
                   &:hover{
                       transform: scale(1.3);
                   }
                   &.canvas-icon-active{
                       background-color: rgba(93, 143, 170, 0.6);
                       p{
                           color: #fff;
                       }
                   }
               }
           }
       }
       .canvas-content{
           width: calc(100% - 100px);
           .canvas{
               height: 800px;
               cursor: crosshair;
           }
           .canvas-options{
               border-top: 1px solid #000;
               height: 60px;
               cursor: pointer;
               display: flex;
               align-items: center;
               >div{
                   height: 60px;
                   line-height: 60px;
                   text-align: center;
                   button{
                       width: 80%;
                   }
               }
               .option-change-water-type{
                   width: 10%;
                   /*background-color: #ea5e00;*/
               }
               .option-draw-text{
                   width: 20%;
                   /*background-color: #4beab0;*/
               }
               .option-commit{
                   width: 45%;
                   /*background-color: #76beea;*/
               }
               .option-drag{
                   width: 18%;
               }
               .option-clear{
                   width: 18%;
                   /*background-color: #ea0000;*/
               }
           }
       }

   }
</style>

效果图如下:

image.png



canvas事件

const canvasDraw  = {
    init(element) {
        canvas = document.getElementById(element)
        ctx = canvas.getContext('2d')

        const w = 1200, h = 800;
        canvas.width = w * devicePixelRatio;
        canvas.height = h * devicePixelRatio;
        canvas.style.width = w + 'px';
        canvas.style.height = h + 'px';

        calculationFlow();

        canvas.onmousedown = (e) => canvasMousedown(e);

        canvas.onmouseup = () => {
            canvas.onmousemove = null;
            window.onmousemove = null;
            canvas.style.cursor= "crosshair"
        }
        return canvas
    },

    // 回传鼠标抬起事件
    canvasMouseUp: () => {
        return allElementCollection[current_select_element_index]
    },

    // 更改绘制类型
    drawTypeChange(element) {
        equipment_select = element
        draw_element_type = element.drawType
    },

    // 修改管线类型(冷热水)
    changePipelineType: (type) => {
        pipeline_water_type = type
    },

    // 修改信息提交
    canvasModifyInfo: (params, flag) => {
        const shape = allElementCollection[current_select_element_index]
        if (flag.includes('equipmentInfo')) {
            image.src = require(`../assets/images/${params.iconPath}`);
            let icon_width = Math.ceil(image.width / params.scale),
                icon_height = Math.ceil(image.height / params.scale);
            shape.endX = shape.startX + icon_width;
            shape.endY = shape.startY + icon_height;
        }
        if (flag.includes('textInfo')) {
            ctx.font = `normal normal normal ${params.fontSize + 'px' || '16px'} Microsoft YaHei`;
            const measureText = ctx.measureText(params.text);
            const textW = measureText.width,
                textH = measureText.actualBoundingBoxAscent + measureText.actualBoundingBoxDescent;
            shape.endX = shape.startX + textW;
            shape.endY = shape.startY - textH;
        }
        allElementCollection[current_select_element_index][flag] = params;
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        draw();
    },

    // 显示设备可拖动的区域范围
    showEquipmentIconArea: ()  => {
        equipment_area_show = !equipment_area_show;
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        draw();
    },

    commit: () => {
        // todo
        // 提交事件


        console.log(allElementCollection)
    },
    
    //回显
    echoData(data) {
        //下方为前后端正式对接所用数据格式 回显处理
        // let echoData = []
        // data.map(item => {
        //     let echoItem = new PipelineFactory()
        //     for (let i in item) {
        //         echoItem[i] = item[i]
        //     }
        //     echoData.push(echoItem)
        //     // console.log(echoItem);
        // })
        // allElementCollection = echoData
    },
    
    // 清除所有
    clearAll: () => {
        allElementCollection = []
        ctx.clearRect(0, 0, canvas.width, canvas.height);
    }
}

好了,综合前面 4 节,这个功能就全部结束了,如果这个功能对您有所帮助的话,还恳请您点赞、评论、及收藏。谢谢!

最后,附上源码地址: https://gitee.com/zhuzio/online-pipeline.git ;

欢迎叨扰,请邮件:1483364913@qq.com;



版权声明:本文为qq_37070696原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。