canvas库konvajs制作画板功能 类似QQ截图 可拖动

konvajs制作画板功能 类似QQ截图 可拖动

demo演示

截图演示


画板palette.gif

一、变量申明

let draw=[], // 绘制的图形数组
    graphNow=null, // 当前图形
    flag=null, // 激活绘制-铅笔 pencil:铅笔 ellipse:椭圆 rect:矩形 rectH:矩形-空心
    drawing=false, // 绘制中
    graphColor='red', // 默认颜色
    pointStart=[]; // 初始坐标

二、获得Konva对象

// 1 create stage
const stage=new Konva.Stage({
    container: 'container',
    width: 1200,
    height: 800
});

// 2 create layer
const layer=new Konva.Layer();
stage.add(layer);

// 3 create our shape

// 移除改变大小事件
stage.on('mousedown', function(e) {
    // 如果点击空白处 移除图形选择框
    // console.log(e);

    if (e.target === stage) {
        stageMousedown(flag, e);

        // 移除图形选择框
        stage.find('Transformer').destroy();
        layer.draw();
        return;
    }
    // 如果没有匹配到就终止往下执行
    if (!e.target.hasName('line') && !e.target.hasName('ellipse') && !e.target.hasName('rect') && !e.target.hasName('circle')) {
        return;
    }
    // 移除图形选择框
    stage.find('Transformer').destroy();

    // 当前点击的对象赋值给graphNow
    graphNow=e.target;
    // 创建图形选框事件
    const tr = new Konva.Transformer({
        borderStroke: '#000', // 虚线颜色
        borderStrokeWidth: 1, //虚线大小
        borderDash: [5], // 虚线间距
        keepRatio: false // 不等比缩放
    });
    layer.add(tr);
    tr.attachTo(e.target);
    layer.draw();
});

// 鼠标移动
    stage.on('mousemove', function (e) {
        if (graphNow && flag && drawing) {
            stageMousemove(flag, e);
        }
    });

    // 鼠标放开
    stage.on('mouseup', function () {
        drawing=false;
        if (flag === 'text') flag=null;
    });

三、绘制

1.铅笔

// 铅笔
// @param points 点数组
// @param stroke 颜色
// @param strokeWidth 线粗细

function drawPencil(points, stroke, strokeWidth) {
    const line = new Konva.Line({
        name: 'line',
        points: points,
        stroke: stroke,
        strokeWidth: strokeWidth,
        lineCap: 'round',
        lineJoin: 'round',
        tension: 0.5,
        draggable: true
    });
    graphNow=line;
    layer.add(line);
    layer.draw();

    line.on('mouseenter', function() {
        stage.container().style.cursor = 'move';
    });

    line.on('mouseleave', function() {
        stage.container().style.cursor = 'default';
    });

    line.on('dblclick', function() {
        // 双击删除自己
        this.remove();
        stage.find('Transformer').destroy();
        layer.draw();
    });
}

2.椭圆

 // 椭圆
 // @param x x坐标
 // @param y y坐标
 // @param rx x半径
 // @param ry y半径
 // @param stroke 描边颜色
 // @param strokeWidth 描边大小
 
function drawEllipse(x, y, rx, ry, stroke, strokeWidth) {
    const ellipse=new Konva.Ellipse({
        name: 'ellipse',
        x: x,
        y: y,
        radiusX: rx,
        radiusY: ry,
        stroke: stroke,
        strokeWidth: strokeWidth,
        draggable: true
    });
    graphNow=ellipse;
    layer.add(ellipse);
    layer.draw();

    ellipse.on('mouseenter', function() {
        stage.container().style.cursor = 'move';
    });

    ellipse.on('mouseleave', function() {
        stage.container().style.cursor = 'default';
    });

    ellipse.on('dblclick', function() {
        // 双击删除自己
        this.remove();
        stage.find('Transformer').destroy();
        layer.draw();
    });
}

3.绘制矩形

/**
 * 矩形
 * @param x x坐标
 * @param y y坐标
 * @param w 宽
 * @param h 高
 * @param c 颜色
 * @param sw 该值大于0-表示空心矩形(描边宽),等于0-表示实心矩形
 */
function drawRect(x, y, w, h, c, sw) {
    const rect = new Konva.Rect({
        name: 'rect',
        x: x,
        y: y,
        width: w,
        height: h,
        fill: sw===0?c:null,
        stroke: sw>0?c:null,
        strokeWidth: sw,
        opacity: sw===0?0.5:1,
        draggable: true
    });
    graphNow=rect;
    layer.add(rect);
    layer.draw();

    rect.on('mouseenter', function() {
        stage.container().style.cursor = 'move';
    });

    rect.on('mouseleave', function() {
        stage.container().style.cursor = 'default';
    });

    rect.on('dblclick', function() {
        // 双击删除自己
        this.remove();
        stage.find('Transformer').destroy();
        layer.draw();
    });
}

4.文字

/**
 * 输入文字
 * @param x x坐标
 * @param y y坐标
 * @param fill 文字颜色
 * @param fs 文字大小
 */
function drawText(x, y, fill, fs) {
    var text = new Konva.Text({
        text: '双击编辑文字',
        x: x,
        y: y,
        fill: fill,
        fontSize: fs,
        width: 300,
        draggable: true
    });
    graphNow=text;
    layer.add(text);
    layer.draw();

    text.on('mouseenter', function() {
        stage.container().style.cursor = 'move';
    });

    text.on('mouseleave', function() {
        stage.container().style.cursor = 'default';
    });

    text.on('dblclick', function() {
        // 在画布上创建具有绝对位置的textarea

        // 首先,我们需要为textarea找到位置

        // 首先,让我们找到文本节点相对于舞台的位置:
        let textPosition = this.getAbsolutePosition();

        // 然后让我们在页面上找到stage容器的位置
        let stageBox = stage.container().getBoundingClientRect();

        // 因此textarea的位置将是上面位置的和
        let areaPosition = {
            x: stageBox.left + textPosition.x,
            y: stageBox.top + textPosition.y
        };

        // 创建textarea并设置它的样式
        let textarea = document.createElement('textarea');
        document.body.appendChild(textarea);

        let T=this.text();
        if (T === '双击编辑文字') {
            textarea.value = '';
            textarea.setAttribute('placeholder','请输入文字')
        } else {
            textarea.value = T;
        }

        textarea.style.position = 'absolute';
        textarea.style.top = areaPosition.y + 'px';
        textarea.style.left = areaPosition.x + 'px';
        textarea.style.background = 'none';
        textarea.style.border = '1px dashed #000';
        textarea.style.outline = 'none';
        textarea.style.color = this.fill();
        textarea.style.width = this.width();

        textarea.focus();

        this.setAttr('text', '');
        layer.draw();

        // 确定输入的文字
        let confirm=(val) => {
            this.text(val?val:'双击编辑文字');
            layer.draw();
            // 隐藏在输入
            if (textarea) document.body.removeChild(textarea);
        };
        // 回车键
        let keydown=(e) => {
            if (e.keyCode === 13) {
                textarea.removeEventListener('blur', blur);
                confirm(textarea.value)
            }
        };
        // 鼠标失去焦点
        let blur=() => {
            textarea.removeEventListener('keydown', keydown);
            confirm(textarea.value);
        };

        textarea.addEventListener('keydown', keydown);
        textarea.addEventListener('blur', blur);
    });
}

5.鼠标按下

/**
 * stage鼠标按下
 * @param flag 是否可绘制
 * @param ev 传入的event对象
 */
function stageMousedown(flag, ev) {
    if (flag) {
        let x=ev.evt.offsetX, y=ev.evt.offsetY;
        pointStart=[x, y];

        switch (flag) {
            case 'pencil':
                drawPencil(pointStart, graphColor, 2);
                break;
            case 'ellipse':
                // 椭圆
                drawEllipse(x, y, 0, 0, graphColor, 2);
                break;
            case 'rect':
                drawRect(x, y, 0, 0, graphColor, 0);
                break;
            case 'rectH':
                drawRect(x, y, 0, 0, graphColor, 2);
                break;
            case 'text':
                drawText(x, y, graphColor, 16);
                break;
            default:
                break;
        }
        drawing=true;
    }
}

6.鼠标移动

/**
 * stage鼠标移动
 * @param flag 是否可绘制
 * @param ev 传入的event对象
 */
function stageMousemove(flag, ev) {
    switch (flag) {
        case 'pencil':
            // 铅笔
            pointStart.push(ev.evt.offsetX, ev.evt.offsetY);
            graphNow.setAttrs({
                points: pointStart
            });
            break;
        case 'ellipse':
            // 椭圆
            graphNow.setAttrs({
                radiusX: Math.abs(ev.evt.offsetX-pointStart[0]),
                radiusY: Math.abs(ev.evt.offsetY-pointStart[1])
            });
            break;
        case 'rect':
        case 'rectH':
            graphNow.setAttrs({
                width: ev.evt.offsetX-pointStart[0],
                height: ev.evt.offsetY-pointStart[1]
            });
            break;
        default:
            break;
    }
    layer.draw();
}

7.选择颜色

// 选择颜色
function selectColorFn(t) {
    graphColor=t.value;
}

8.删除

// 移除图形
function removeFn() {
    if (graphNow) {
        graphNow.remove();
        stage.find('Transformer').destroy();
        layer.draw();
        graphNow=null;
    } else {
        alert('请选择图形')
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,198评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,663评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,985评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,673评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,994评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,399评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,717评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,407评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,112评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,371评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,891评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,255评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,881评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,010评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,764评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,412评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,299评论 2 260

推荐阅读更多精彩内容