vue项目中基于fabric 插件实现涂鸦画布功能

发布于:2024-04-28 ⋅ 阅读:(59) ⋅ 点赞:(0)

vue项目中基于fabric 插件实现涂鸦画布功能

一、效果图

在这里插入图片描述

二、安装依赖

 npm install fabric 

三、main.js引入

import fabric from 'fabric'
Vue.use(fabric);

四、主要代码

//封装成了一个组件
<template>
    <el-dialog
        title="涂鸦生图"
        :visible="visible"
        custom-class="doodleDialog"
        @close="handleClose"
        @open="openDialog"
        width="1500px"
    >
        <div style="display: flex; justify-content: space-between">
            <div class="rigth">
                <p style="font-size: 16px">涂鸦区</p>
                <div class="maintenancePlanAdd">
                    <div class="child-panel-title"></div>
                    <div class="panel-body">
                        <div class="demo">
                            <canvas id="canvas" :width="width" :height="height"></canvas>
                            <div class="draw-btn-group">
                                <div
                                    :class="{ active: drawType == '' }"
                                    title="自由选择"
                                    @click="drawTypeChange('')"
                                >
                                    <i class="draw-icon icon-mouse"></i>
                                </div>
                                <div
                                    :class="{ active: drawType == 'arrow' }"
                                    title="画箭头"
                                    @click="drawTypeChange('arrow')"
                                >
                                    <i class="draw-icon icon-1"></i>
                                </div>
                                <div
                                    :class="{ active: drawType == 'text' }"
                                    title="文本输入框"
                                    @click="drawTypeChange('text')"
                                >
                                    <i class="draw-icon icon-2"></i>
                                </div>
                                <div
                                    :class="{ active: drawType == 'ellipse' }"
                                    title="画圆"
                                    @click="drawTypeChange('ellipse')"
                                >
                                    <i class="draw-icon icon-3"></i>
                                </div>
                                <div
                                    :class="{ active: drawType == 'rectangle' }"
                                    title="画矩形"
                                    @click="drawTypeChange('rectangle')"
                                >
                                    <i class="draw-icon icon-4"></i>
                                </div>
                                <div
                                    :class="{ active: drawType == 'polygon' }"
                                    title="画多边形"
                                    @click="drawPolygon"
                                >
                                    <i class="draw-icon icon-6"></i>
                                </div>
                                <div
                                    :class="{ active: drawType == 'pen' }"
                                    title="笔画"
                                    @click="drawTypeChange('pen')"
                                >
                                    <i class="draw-icon icon-7"></i>
                                </div>
                                <div
                                    :class="{ active: drawType == 'pentagram' }"
                                    title="五角星"
                                    @click="drawTypeChange('pentagram')"
                                >
                                    <i class="draw-icon icon-pentagram"></i>
                                </div>
                                <div
                                    :class="{ active: drawType == 'delete' }"
                                    title="删除"
                                    @click="drawTypeDelete()"
                                >
                                    <i style="font-size: 26px" class="el-icon-delete"></i>
                                </div>
                                <!-- <div @click="uploadImg" title="从文件选择图片上传">
              <i class="draw-icon icon-img"></i>
            </div>
            <div @click="loadExpImg" title="加载背景图">
              <i class="draw-icon icon-back"></i>
            </div>
            <div @click="save" title="保存">
              <i class="draw-icon icon-save"></i>
            </div> -->
                            </div>
                        </div>
                    </div>
                    <input type="file" @change="uploadImgChange" id="imgInput" accept="image/*" />
                    <img id="img" :src="imgSrc" />
                    <img id="expImg" src="../../../assets/images/draw/exp.jpg" />
                </div>
            </div>
            <div class="left" style="width: 600px">
                <p style="font-size: 16px">生成区</p>
                <div style="border: 1px dashed black">
                    <p style="text-align: center; margin-top: 5px">生成图片如下:</p>
                    <div style="width: 598px; height: 400px; margin-top: 18px; margin-bottom: 58px">
                        <img
                            v-if="resultImg"
                            style="width: 598px; height: 400px; display: inline-block"
                            :src="resultImg"
                            alt=""
                        />
                    </div>
                    <el-form
                        class="screenwaper"
                        :model="addInnerFrom"
                        :rules="addRules"
                        ref="addInnerFrom"
                        label-width="90px"
                        label-position="rigth"
                    >
                        <el-form-item label="文字描述:" prop="prompt">
                            <el-input
                                style="width: 500px"
                                v-model="addInnerFrom.prompt"
                                placeholder="请输入"
                                size="small"
                                type="textarea"
                                :rows="2"
                            ></el-input>
                        </el-form-item>
                        <el-form-item label="相似度:" prop="similarity">
                            <el-slider
                                style="width: 500px"
                                v-model="addInnerFrom.similarity"
                                :format-tooltip="formatTooltip"
                            ></el-slider>
                        </el-form-item>
                        <p style="text-align: center; margin: 20px 0 30px">
                            <el-button
                                size="small"
                                type="primary"
                                :loading="loading"
                                @click="handleSureDialog('addInnerFrom')"
                            >
                                确定生成
                            </el-button>
                        </p>
                    </el-form>
                </div>
            </div>
        </div>

        <div style="text-align: center; padding: 20px 0 0">
            <el-button size="small" @click="handleSure">关 闭</el-button>
        </div>
    </el-dialog>
</template>

<script>
import { fabric } from 'fabric';
import { doodleImg } from '../api';
export default {
    name: 'doodleDialog',
    props: {
        visible: {
            type: Boolean,
            default: false,
        },
    },
    data() {
        return {
            addInnerFrom: { prompt: '', imageUploadData: '', similarity: 0 },
            addRules: {
                prompt: [{ required: true, message: '请输入标题', trigger: 'blur' }],
                similarity: [{ required: true, message: '请输入文本描述', trigger: 'blur' }],
            },
            loading: false,

            width: 800,
            height: 700,
            rect: [],
            canvas: {},
            showMenu: false,
            x: '',
            y: '',

            mouseFrom: {},
            mouseTo: {},
            drawType: null, //当前绘制图像的种类
            canvasObjectIndex: 0,
            textbox: null,
            rectangleLabel: 'warning',
            drawWidth: 2, //笔触宽度
            color: '#E34F51', //画笔颜色
            drawingObject: null, //当前绘制对象
            moveCount: 1, //绘制移动计数器
            doDrawing: false, // 绘制状态

            //polygon 相关参数
            polygonMode: false,
            pointArray: [],
            lineArray: [],
            activeShape: false,
            activeLine: '',
            line: {},

            delectKlass: {},
            imgFile: {},
            imgSrc: '',
            resultImg: '',
        };
    },
    watch: {
        drawType() {
            this.canvas.selection = !this.drawType;
        },
        width() {
            this.canvas.setWidth(this.width);
        },
        height() {
            this.canvas.setHeight(this.height);
        },
    },
    methods: {
        formatTooltip(val) {
            return val / 100;
        },
        openDialog() {
            this.resultImg = '';
            this.loading = false;
            this.addInnerFrom = { prompt: '', imageUploadData: '', similarity: 0 };
            this.$nextTick(() => {
                this.canvas = new fabric.Canvas('canvas', {
                    // skipTargetFind: false, //当为真时,跳过目标检测。目标检测将返回始终未定义。点击选择将无效
                    // selectable: false,  //为false时,不能选择对象进行修改
                    // selection: false   // 是否可以多个对象为一组
                });
                this.canvas.selectionColor = 'rgba(0,0,0,0.05)';
                this.canvas.on('mouse:down', this.mousedown);
                this.canvas.on('mouse:move', this.mousemove);
                this.canvas.on('mouse:up', this.mouseup);

                document.onkeydown = e => {
                    // 键盘 delect删除所选元素
                    if (e.keyCode == 46) {
                        this.deleteObj();
                    }
                    // ctrl+z 删除最近添加的元素
                    if (e.keyCode == 90 && e.ctrlKey) {
                        this.canvas.remove(
                            this.canvas.getObjects()[this.canvas.getObjects().length - 1]
                        );
                    }
                };
            });
        },
        //  画布下面删除按钮
        drawTypeDelete() {
            this.drawType = 'delete';
            this.canvas.clear();
        },
        handleClose() {
            this.canvas.clear();
            this.$emit('DialogCancel');
        },
        handleSure() {
            this.handleClose();
            this.$emit('DialogOk', this.resultImg);
        },
        handleSureDialog(From) {
            this.$refs[From].validate(valid => {
                if (valid) {
                    this.loading = true;
                    let canvas = document.getElementById('canvas');
                    this.addInnerFrom.imageUploadData = canvas.toDataURL('png');
                    doodleImg({
                        imageFile: this.addInnerFrom.imageUploadData,
                        prompt: this.addInnerFrom.prompt,
                        similarity: this.formatTooltip(this.addInnerFrom.similarity),
                    }).then(({ data }) => {
                        if (data.length) {
                            this.loading = false;
                            this.resultImg = data[0];
                            this.$message({
                                showClose: true,
                                message: `已成功生成图片${data.length}`,
                                type: 'success',
                            });
                        }
                    });
                } else {
                    console.log('error submit!!');
                    return false;
                }
            });
        },
        // 保存当前画布为png图片
        save() {
            let canvas = document.getElementById('canvas');
            let imgData = canvas.toDataURL('png');
            console.log(imgData, 'wwww');
            imgData = imgData.replace('image/png', 'image/octet-stream');

            // 下载后的问题名,可自由指定
            let filename = 'drawingboard_' + new Date().getTime() + '.' + 'png';
            this.saveFile(imgData, filename);
        },
        saveFile(data, filename) {
            let save_link = document.createElement('a');
            save_link.href = data;
            save_link.download = filename;

            let event = document.createEvent('MouseEvents');
            event.initMouseEvent(
                'click',
                true,
                false,
                window,
                0,
                0,
                0,
                0,
                0,
                false,
                false,
                false,
                false,
                0,
                null
            );
            save_link.dispatchEvent(event);
        },
        uploadImg() {
            document.getElementById('imgInput').click();
        },
        // 从已渲染的DOM元素加载图片至canvas
        loadExpImg() {
            let imgElement = document.getElementById('expImg'); //声明我们的图片
            let imgInstance = new fabric.Image(imgElement, {
                selectable: false,
                // zIndex:-99,
            });
            this.canvas.add(imgInstance);
        },
        // 从文件加载图片至canvas
        uploadImgChange() {
            // 获取文件
            let eleImportInput = document.getElementById('imgInput');
            this.imgFile = eleImportInput.files[0];
            let imgSrc = '',
                imgTitle = '';
            // 从reader中获取选择文件的src
            if (/\.(jpe?g|png|gif)$/i.test(this.imgFile.name)) {
                let reader = new FileReader();
                let _this = this;
                reader.addEventListener(
                    'load',
                    function () {
                        imgTitle = _this.imgFile.name;
                        _this.imgSrc = this.result;
                    },
                    false
                );
                reader.readAsDataURL(this.imgFile);
            }
            let imgElement = document.getElementById('img'); //声明我们的图片

            imgElement.onload = () => {
                this.width = imgElement.width;
                this.height = imgElement.height;
                let imgInstance = new fabric.Image(imgElement, {
                    zIndex: -1,
                    selectable: false,
                });
                this.canvas.add(imgInstance);
            };
        },
        // 开始绘制时,指定绘画种类
        drawTypeChange(e) {
            this.drawType = e;
            this.canvas.skipTargetFind = !!e;
            if (e == 'pen') {
                // isDrawingMode为true 才可以自由绘画
                this.canvas.isDrawingMode = true;
            } else {
                this.canvas.isDrawingMode = false;
            }
        },
        // 鼠标按下时触发
        mousedown(e) {
            // 记录鼠标按下时的坐标
            let xy = e.pointer || this.transformMouse(e.e.offsetX, e.e.offsetY);
            this.mouseFrom.x = xy.x;
            this.mouseFrom.y = xy.y;
            this.doDrawing = true;
            if (this.drawType == 'text') {
                this.drawing();
            }

            if (this.textbox) {
                this.textbox.enterEditing();
                this.textbox.hiddenTextarea.focus();
            }
            // 绘制多边形
            if (this.drawType == 'polygon') {
                this.canvas.skipTargetFind = false;
                try {
                    // 此段为判断是否闭合多边形,点击红点时闭合多边形
                    if (this.pointArray.length > 1) {
                        // e.target.id == this.pointArray[0].id 表示点击了初始红点
                        if (e.target && e.target.id == this.pointArray[0].id) {
                            this.generatePolygon();
                        }
                    }
                    //未点击红点则继续作画
                    if (this.polygonMode) {
                        this.addPoint(e);
                    }
                } catch (error) {
                    console.log(error);
                }
            }
        },
        // 鼠标松开执行
        mouseup(e) {
            let xy = e.pointer || this.transformMouse(e.e.offsetX, e.e.offsetY);
            this.mouseTo.x = xy.x;
            this.mouseTo.y = xy.y;
            this.drawingObject = null;
            this.moveCount = 1;
            if (this.drawType != 'polygon') {
                this.doDrawing = false;
            }
        },

        //鼠标移动过程中已经完成了绘制
        mousemove(e) {
            if (this.moveCount % 2 && !this.doDrawing) {
                //减少绘制频率
                return;
            }
            this.moveCount++;
            let xy = e.pointer || this.transformMouse(e.e.offsetX, e.e.offsetY);
            this.mouseTo.x = xy.x;
            this.mouseTo.y = xy.y;
            // 多边形与文字框特殊处理
            if (this.drawType != 'text' || this.drawType != 'polygon') {
                this.drawing(e);
            }
            if (this.drawType == 'polygon') {
                if (this.activeLine && this.activeLine.class == 'line') {
                    let pointer = this.canvas.getPointer(e.e);
                    this.activeLine.set({ x2: pointer.x, y2: pointer.y });

                    let points = this.activeShape.get('points');
                    points[this.pointArray.length] = {
                        x: pointer.x,
                        y: pointer.y,
                        zIndex: 1,
                    };
                    this.activeShape.set({
                        points: points,
                    });
                    this.canvas.renderAll();
                }
                this.canvas.renderAll();
            }
        },
        deleteObj() {
            this.canvas.getActiveObjects().map(item => {
                this.canvas.remove(item);
            });
        },
        transformMouse(mouseX, mouseY) {
            return { x: mouseX / 1, y: mouseY / 1 };
        },
        // 绘制多边形开始,绘制多边形和其他图形不一样,需要单独处理
        drawPolygon() {
            this.drawType = 'polygon';
            this.polygonMode = true;
            //这里画的多边形,由顶点与线组成
            this.pointArray = new Array(); // 顶点集合
            this.lineArray = new Array(); //线集合
            this.canvas.isDrawingMode = false;
        },
        addPoint(e) {
            let random = Math.floor(Math.random() * 10000);
            let id = new Date().getTime() + random;
            let circle = new fabric.Circle({
                radius: 5,
                fill: '#ffffff',
                stroke: '#333333',
                strokeWidth: 0.5,
                left: (e.pointer.x || e.e.layerX) / this.canvas.getZoom(),
                top: (e.pointer.y || e.e.layerY) / this.canvas.getZoom(),
                selectable: false,
                hasBorders: false,
                hasControls: false,
                originX: 'center',
                originY: 'center',
                id: id,
                objectCaching: false,
            });
            if (this.pointArray.length == 0) {
                circle.set({
                    fill: 'red',
                });
            }
            let points = [
                (e.pointer.x || e.e.layerX) / this.canvas.getZoom(),
                (e.pointer.y || e.e.layerY) / this.canvas.getZoom(),
                (e.pointer.x || e.e.layerX) / this.canvas.getZoom(),
                (e.pointer.y || e.e.layerY) / this.canvas.getZoom(),
            ];

            this.line = new fabric.Line(points, {
                strokeWidth: 2,
                fill: '#999999',
                stroke: '#999999',
                class: 'line',
                originX: 'center',
                originY: 'center',
                selectable: false,
                hasBorders: false,
                hasControls: false,
                evented: false,

                objectCaching: false,
            });
            if (this.activeShape) {
                let pos = this.canvas.getPointer(e.e);
                let points = this.activeShape.get('points');
                points.push({
                    x: pos.x,
                    y: pos.y,
                });
                let polygon = new fabric.Polygon(points, {
                    stroke: '#333333',
                    strokeWidth: 1,
                    fill: '#cccccc',
                    opacity: 0.3,

                    selectable: false,
                    hasBorders: false,
                    hasControls: false,
                    evented: false,
                    objectCaching: false,
                });
                this.canvas.remove(this.activeShape);
                this.canvas.add(polygon);
                this.activeShape = polygon;
                this.canvas.renderAll();
            } else {
                let polyPoint = [
                    {
                        x: (e.pointer.x || e.e.layerX) / this.canvas.getZoom(),
                        y: (e.pointer.y || e.e.layerY) / this.canvas.getZoom(),
                    },
                ];
                let polygon = new fabric.Polygon(polyPoint, {
                    stroke: '#333333',
                    strokeWidth: 1,
                    fill: '#cccccc',
                    opacity: 0.3,

                    selectable: false,
                    hasBorders: false,
                    hasControls: false,
                    evented: false,
                    objectCaching: false,
                });
                this.activeShape = polygon;
                this.canvas.add(polygon);
            }
            this.activeLine = this.line;

            this.pointArray.push(circle);
            this.lineArray.push(this.line);
            this.canvas.add(this.line);
            this.canvas.add(circle);
        },
        generatePolygon() {
            let points = new Array();
            this.pointArray.map((point, index) => {
                points.push({
                    x: point.left,
                    y: point.top,
                });
                this.canvas.remove(point);
            });
            this.lineArray.map((line, index) => {
                this.canvas.remove(line);
            });
            this.canvas.remove(this.activeShape).remove(this.activeLine);
            let polygon = new fabric.Polygon(points, {
                stroke: this.color,
                strokeWidth: this.drawWidth,
                fill: 'rgba(255, 255, 255, 0)',
                opacity: 1,
                hasBorders: true,
                hasControls: false,
            });
            this.canvas.add(polygon);

            this.activeLine = null;
            this.activeShape = null;
            this.polygonMode = false;
            this.doDrawing = false;
            this.drawType = null;
        },
        drawing(e) {
            if (this.drawingObject) {
                this.canvas.remove(this.drawingObject);
            }
            let canvasObject = null;
            let left = this.mouseFrom.x,
                top = this.mouseFrom.y,
                mouseFrom = this.mouseFrom,
                mouseTo = this.mouseTo;
            switch (this.drawType) {
                case 'arrow':
                    {
                        //箭头
                        let x1 = mouseFrom.x,
                            x2 = mouseTo.x,
                            y1 = mouseFrom.y,
                            y2 = mouseTo.y;
                        let w = x2 - x1,
                            h = y2 - y1,
                            sh = Math.cos(Math.PI / 4) * 16;
                        let sin = h / Math.sqrt(Math.pow(w, 2) + Math.pow(h, 2));
                        let cos = w / Math.sqrt(Math.pow(w, 2) + Math.pow(h, 2));
                        let w1 = (16 * sin) / 4,
                            h1 = (16 * cos) / 4,
                            centerx = sh * cos,
                            centery = sh * sin;
                        /**
                         * centerx,centery 表示起始点,终点连线与箭头尖端等边三角形交点相对x,y
                         * w1 ,h1用于确定四个点
                         */

                        let path = ' M ' + x1 + ' ' + y1;
                        path += ' L ' + (x2 - centerx + w1) + ' ' + (y2 - centery - h1);
                        path += ' L ' + (x2 - centerx + w1 * 2) + ' ' + (y2 - centery - h1 * 2);
                        path += ' L ' + x2 + ' ' + y2;
                        path += ' L ' + (x2 - centerx - w1 * 2) + ' ' + (y2 - centery + h1 * 2);
                        path += ' L ' + (x2 - centerx - w1) + ' ' + (y2 - centery + h1);
                        path += ' Z';
                        canvasObject = new fabric.Path(path, {
                            stroke: this.color,
                            fill: this.color,
                            strokeWidth: this.drawWidth,
                        });
                    }
                    break;
                case 'pentagram':
                    {
                        //五角星
                        let x1 = mouseFrom.x,
                            x2 = mouseTo.x,
                            y1 = mouseFrom.y,
                            y2 = mouseTo.y;
                        /**
                         * 实现思路  (x1,y1)表示鼠标起始的位置 (x2,y2)表示鼠标抬起的位置
                         * r 表示五边形外圈圆的半径,这里建议自己画个图理解
                         * 正五边形夹角为36度。计算出cos18°,sin18°备用
                         */
                        let w = Math.abs(x2 - x1),
                            h = Math.abs(y2 - y1),
                            r = Math.sqrt(w * w + h * h);
                        let cos18 = Math.cos((18 * Math.PI) / 180);
                        let sin18 = Math.sin((18 * Math.PI) / 180);

                        /**
                         * 算出对应五个点的坐标转化为路径
                         */
                        let point1 = [x1, y1 + r];
                        let point2 = [x1 + 2 * r * sin18, y1 + r - 2 * r * cos18];
                        let point3 = [x1 - r * cos18, y1 + r * sin18];
                        let point4 = [x1 + r * cos18, y1 + r * sin18];
                        let point5 = [x1 - 2 * r * sin18, y1 + r - 2 * r * cos18];

                        let path = ' M ' + point1[0] + ' ' + point1[1];
                        path += ' L ' + point2[0] + ' ' + point2[1];
                        path += ' L ' + point3[0] + ' ' + point3[1];
                        path += ' L ' + point4[0] + ' ' + point4[1];
                        path += ' L ' + point5[0] + ' ' + point5[1];
                        path += ' Z';
                        canvasObject = new fabric.Path(path, {
                            stroke: this.color,
                            fill: this.color,
                            strokeWidth: this.drawWidth,
                            // angle:180,  //设置旋转角度
                        });
                    }
                    break;
                case 'ellipse':
                    {
                        //椭圆
                        // 按shift时画正圆,只有在鼠标移动时才执行这个,所以按了shift但是没有拖动鼠标将不会画圆
                        if (e.e.shiftKey) {
                            mouseTo.x - left > mouseTo.y - top
                                ? (mouseTo.y = top + mouseTo.x - left)
                                : (mouseTo.x = left + mouseTo.y - top);
                        }
                        let radius =
                            Math.sqrt(
                                (mouseTo.x - left) * (mouseTo.x - left) +
                                    (mouseTo.y - top) * (mouseTo.y - top)
                            ) / 2;
                        canvasObject = new fabric.Ellipse({
                            left: (mouseTo.x - left) / 2 + left,
                            top: (mouseTo.y - top) / 2 + top,
                            stroke: this.color,
                            fill: 'rgba(255, 255, 255, 0)',
                            originX: 'center',
                            originY: 'center',
                            rx: Math.abs(left - mouseTo.x) / 2,
                            ry: Math.abs(top - mouseTo.y) / 2,
                            strokeWidth: this.drawWidth,
                        });
                    }
                    break;
                case 'rectangle':
                    {
                        //长方形
                        // 按shift时画正方型
                        if (e.e.shiftKey) {
                            mouseTo.x - left > mouseTo.y - top
                                ? (mouseTo.y = top + mouseTo.x - left)
                                : (mouseTo.x = left + mouseTo.y - top);
                        }
                        let path =
                            'M ' +
                            mouseFrom.x +
                            ' ' +
                            mouseFrom.y +
                            ' L ' +
                            mouseTo.x +
                            ' ' +
                            mouseFrom.y +
                            ' L ' +
                            mouseTo.x +
                            ' ' +
                            mouseTo.y +
                            ' L ' +
                            mouseFrom.x +
                            ' ' +
                            mouseTo.y +
                            ' L ' +
                            mouseFrom.x +
                            ' ' +
                            mouseFrom.y +
                            ' z';
                        canvasObject = new fabric.Path(path, {
                            left: left,
                            top: top,
                            stroke: this.color,
                            strokeWidth: this.drawWidth,
                            fill: 'rgba(255, 255, 255, 0)',
                            hasControls: false,
                        });
                    }
                    //也可以使用fabric.Rect
                    break;
                case 'text':
                    {
                        //文本框
                        this.textbox = new fabric.Textbox('', {
                            left: mouseFrom.x,
                            top: mouseFrom.y - 10,
                            // width: 150,
                            fontSize: 16,
                            borderColor: this.color,
                            fill: this.color,
                            hasControls: false,
                        });
                        this.canvas.add(this.textbox);
                        this.textbox.enterEditing();
                        this.textbox.hiddenTextarea.focus();
                    }
                    break;

                default:
                    break;
            }

            if (canvasObject) {
                // canvasObject.index = getCanvasObjectIndex();\
                this.canvas.add(canvasObject); //.setActiveObject(canvasObject)
                this.drawingObject = canvasObject;
            }
        },
    },
};
</script>

<style lang="scss" scope>
.doodleDialog {
    .el-container {
        flex-direction: column;
    }
    img,
    input {
        display: none;
    }
    .demo {
        display: flex;
        flex-direction: column;
        align-items: center;
    }
    canvas {
        border: 1px dashed black;
    }
    .draw-btn-group {
        // width: 1270px;
        margin-top: 10px;
        display: flex;
        align-items: center;
        justify-content: flex-start;
        & > div {
            background: #fafafa;
            cursor: pointer;
            &:hover {
                background: #eee;
            }
            i {
                display: flex;
                background-repeat: no-repeat;
                background-size: 80%;
                background-position: 50% 50%;
                height: 30px;
                width: 30px;
            }
            .icon-1 {
                background-image: url('../../../assets/images/draw/1.png');
            }
            .icon-pentagram {
                background-image: url('../../../assets/images/draw/pentagram.png');
            }
            .icon-2 {
                background-image: url('../../../assets/images/draw/2.png');
            }
            .icon-3 {
                background-image: url('../../../assets/images/draw/3.png');
            }
            .icon-4 {
                background-image: url('../../../assets/images/draw/4.png');
                background-size: 75%;
            }
            .icon-5 {
                background-image: url('../../../assets/images/draw/5.png');
                background-size: 70%;
            }
            .icon-6 {
                background-image: url('../../../assets/images/draw/6.png');
            }
            .icon-7 {
                background-image: url('../../../assets/images/draw/7.png');
                background-size: 80%;
            }
            .icon-del {
                background-image: url('../../../assets/images/draw/del.png');
                background-size: 90%;
            }
            .icon-img {
                background-image: url('../../../assets/images/draw/img.png');
                background-size: 80%;
            }
            .icon-back {
                background-image: url('../../../assets/images/draw/back.png');
                background-size: 75%;
            }
            .icon-save {
                background-image: url('../../../assets/images/draw/save.png');
                background-size: 80%;
            }
            .icon-mouse {
                background-image: url('../../../assets/images/draw/mouse.png');
                background-size: 60%;
            }
        }
        .active {
            background: #eee;
        }
    }
}
</style>
<style lang="scss"></style>

链接: https://www.jianshu.com/p/d6d924eb5cf7
链接: https://github.com/Couy69/vue-fabric-drawingboard


网站公告

今日签到

点亮在社区的每一天
去签到