canvas庫(kù)konvajs制作畫板功能 類似QQ截圖 可拖動(dòng)

konvajs制作畫板功能 類似QQ截圖 可拖動(dòng)

demo演示

截圖演示


畫板palette.gif

一、變量申明

let draw=[], // 繪制的圖形數(shù)組
    graphNow=null, // 當(dāng)前圖形
    flag=null, // 激活繪制-鉛筆 pencil:鉛筆 ellipse:橢圓 rect:矩形 rectH:矩形-空心
    drawing=false, // 繪制中
    graphColor='red', // 默認(rèn)顏色
    pointStart=[]; // 初始坐標(biāo)

二奖磁、獲得Konva對(duì)象

// 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) {
    // 如果點(diǎn)擊空白處 移除圖形選擇框
    // console.log(e);

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

        // 移除圖形選擇框
        stage.find('Transformer').destroy();
        layer.draw();
        return;
    }
    // 如果沒有匹配到就終止往下執(zhí)行
    if (!e.target.hasName('line') && !e.target.hasName('ellipse') && !e.target.hasName('rect') && !e.target.hasName('circle')) {
        return;
    }
    // 移除圖形選擇框
    stage.find('Transformer').destroy();

    // 當(dāng)前點(diǎn)擊的對(duì)象賦值給graphNow
    graphNow=e.target;
    // 創(chuàng)建圖形選框事件
    const tr = new Konva.Transformer({
        borderStroke: '#000', // 虛線顏色
        borderStrokeWidth: 1, //虛線大小
        borderDash: [5], // 虛線間距
        keepRatio: false // 不等比縮放
    });
    layer.add(tr);
    tr.attachTo(e.target);
    layer.draw();
});

// 鼠標(biāo)移動(dòng)
    stage.on('mousemove', function (e) {
        if (graphNow && flag && drawing) {
            stageMousemove(flag, e);
        }
    });

    // 鼠標(biāo)放開
    stage.on('mouseup', function () {
        drawing=false;
        if (flag === 'text') flag=null;
    });

三、繪制

1.鉛筆

// 鉛筆
// @param points 點(diǎn)數(shù)組
// @param stroke 顏色
// @param strokeWidth 線粗細(xì)

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坐標(biāo)
 // @param y y坐標(biāo)
 // @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坐標(biāo)
 * @param y y坐標(biāo)
 * @param w 寬
 * @param h 高
 * @param c 顏色
 * @param sw 該值大于0-表示空心矩形(描邊寬),等于0-表示實(shí)心矩形
 */
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坐標(biāo)
 * @param y y坐標(biāo)
 * @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() {
        // 在畫布上創(chuàng)建具有絕對(duì)位置的textarea

        // 首先,我們需要為textarea找到位置

        // 首先,讓我們找到文本節(jié)點(diǎn)相對(duì)于舞臺(tái)的位置:
        let textPosition = this.getAbsolutePosition();

        // 然后讓我們?cè)陧?yè)面上找到stage容器的位置
        let stageBox = stage.container().getBoundingClientRect();

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

        // 創(chuàng)建textarea并設(shè)置它的樣式
        let textarea = document.createElement('textarea');
        document.body.appendChild(textarea);

        let T=this.text();
        if (T === '雙擊編輯文字') {
            textarea.value = '';
            textarea.setAttribute('placeholder','請(qǐng)輸入文字')
        } 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)
            }
        };
        // 鼠標(biāo)失去焦點(diǎn)
        let blur=() => {
            textarea.removeEventListener('keydown', keydown);
            confirm(textarea.value);
        };

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

5.鼠標(biāo)按下

/**
 * stage鼠標(biāo)按下
 * @param flag 是否可繪制
 * @param ev 傳入的event對(duì)象
 */
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.鼠標(biāo)移動(dòng)

/**
 * stage鼠標(biāo)移動(dòng)
 * @param flag 是否可繪制
 * @param ev 傳入的event對(duì)象
 */
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('請(qǐng)選擇圖形')
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市丰介,隨后出現(xiàn)的幾起案子舵抹,更是在濱河造成了極大的恐慌肪虎,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件惧蛹,死亡現(xiàn)場(chǎng)離奇詭異扇救,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)香嗓,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門迅腔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人靠娱,你說(shuō)我怎么就攤上這事沧烈。” “怎么了像云?”我有些...
    開封第一講書人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵锌雀,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我迅诬,道長(zhǎng)腋逆,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任百框,我火速辦了婚禮闲礼,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘铐维。我一直安慰自己柬泽,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開白布嫁蛇。 她就那樣靜靜地躺著锨并,像睡著了一般。 火紅的嫁衣襯著肌膚如雪睬棚。 梳的紋絲不亂的頭發(fā)上第煮,一...
    開封第一講書人閱讀 51,631評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音抑党,去河邊找鬼包警。 笑死,一個(gè)胖子當(dāng)著我的面吹牛底靠,可吹牛的內(nèi)容都是我干的害晦。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼暑中,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼壹瘟!你這毒婦竟也來(lái)了鲫剿?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤稻轨,失蹤者是張志新(化名)和其女友劉穎灵莲,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體殴俱,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡政冻,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了线欲。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片赠幕。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖询筏,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情竖慧,我是刑警寧澤嫌套,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站圾旨,受9級(jí)特大地震影響踱讨,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜砍的,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一痹筛、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧廓鞠,春花似錦帚稠、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至砌们,卻和暖如春杆麸,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背浪感。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工昔头, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人影兽。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓揭斧,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親赢笨。 傳聞我的和親對(duì)象是個(gè)殘疾皇子未蝌,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容