使用Canvas實(shí)現(xiàn)貝塞爾曲線顯示以及控制

貝塞爾曲線

場(chǎng)景:

創(chuàng)建簡(jiǎn)易的繪圖工具剪决,實(shí)現(xiàn)控制畫布上的四個(gè)錨點(diǎn)的拖拽修改已經(jīng)繪制的曲線圖,通過(guò)注釋實(shí)時(shí)顯示錨點(diǎn)的具體實(shí)時(shí)坐標(biāo),并通過(guò)反色清楚的顯示控制點(diǎn)的位置

要求:
  1. 創(chuàng)建一個(gè)512*512的canvas畫布
  2. 在該畫布上任意創(chuàng)建四個(gè)錨點(diǎn),使用貝塞爾曲線進(jìn)行連接
  3. 可在畫布上自由拖拽上面的錨點(diǎn)彬犯,從而實(shí)現(xiàn)貝塞爾曲線的聯(lián)動(dòng)宁仔,每個(gè)點(diǎn)都有一個(gè)跟隨點(diǎn)移動(dòng)的注釋(文字+箭頭),說(shuō)明該點(diǎn)的坐標(biāo)
  4.在此基礎(chǔ)上斗忌,實(shí)現(xiàn)一個(gè)黑白漸變色的背景闯第,然后在錨點(diǎn)周圍20個(gè)像素內(nèi)逢渔,將錨點(diǎn)所處的背景作反色,突出錨點(diǎn)的顯示

思路整理:

明確:

貝塞爾曲線通過(guò)canvas繪制,canvas自帶貝塞爾曲線繪制方法 bezierCurveTo()

顯示錨點(diǎn)可以在canvas上覆蓋div 將錨點(diǎn)放在div中

錨點(diǎn)拖動(dòng),添加事件監(jiān)聽(tīng), 鼠標(biāo)按下 為被點(diǎn)擊的錨點(diǎn) 添加鼠標(biāo)移動(dòng)事件,松開(kāi) 注銷鼠標(biāo)移動(dòng)事件

拖動(dòng)錨點(diǎn)時(shí)要做什么?

修改注釋內(nèi)的坐標(biāo)

canvas根據(jù)錨點(diǎn)坐標(biāo)重繪貝塞爾曲線

錨點(diǎn)周圍20像素背景色取反

效果展示

效果演示

代碼(隨手寫 可自行優(yōu)化完善)

<style>
    * {
        margin: 0;
        padding: 0;
    }

    #myCanvas {
        position: absolute;
    }

    span {
        position: absolute;
        width: 20px;
        height: 20px;
        background: #21e40f;
        border: 20px solid #fff;
        border-radius: 50%;
        color: rgb(25, 6, 204);
        text-align: center;
        font-size: 14px;
        top: 50px;
        left: 50px;
    }

    #end {
        top: 50px;
        left: 350px;
    }

    #dot1 {
        top: 350px;
        left: 50px;
    }

    #dot2 {
        top: 350px;
        left: 350px;
    }

    i {
        position: absolute;
        display: block;
        width: 60px;
        height: 40px;
        background: rgba(121, 212, 170, 0.3);
        left: 30px;
        top: 30px;
    }
</style>

<body>
    <!-- 要求:
    1. 創(chuàng)建一個(gè)512*512的canvas畫布
    2. 在該畫布上任意創(chuàng)建四個(gè)錨點(diǎn)乡括,使用貝塞爾曲線進(jìn)行連接
    3. 可在畫布上自由拖拽上面的錨點(diǎn),從而實(shí)現(xiàn)貝塞爾曲線的聯(lián)動(dòng)智厌,每個(gè)點(diǎn)都有一個(gè)跟隨點(diǎn)移動(dòng)的注釋(文字+箭頭)诲泌,說(shuō)明該點(diǎn)的坐標(biāo)
    4. 在此基礎(chǔ)上,實(shí)現(xiàn)一個(gè)黑白漸變色的背景铣鹏,然后在錨點(diǎn)周圍20個(gè)像素內(nèi)敷扫,將錨點(diǎn)所處的背景作反色,突出錨點(diǎn)的顯示 -->

    <!-- 畫布 -->
    <canvas id="myCanvas" width="512" height="512" style="border:1px solid #000000;">
    </canvas>
    <!-- 畫布遮罩層  用于錨點(diǎn)顯示控制 -->
    <div id="box">
        <!-- 四個(gè)錨點(diǎn) 開(kāi)始  結(jié)束  錨點(diǎn)1  錨點(diǎn)2-->
        <span id="start">1 <i>1</i></span>
        <span id="dot1">2 <i>2</i></span>
        <span id="dot2">3 <i>3</i></span>
        <span id="end">4 <i>4</i></span>
    </div>

    <script>
        //畫布漸變等
        var c, ctx, grd;
        //四個(gè)錨點(diǎn)
        var start, end, dot1, dot2, liArr;

        //坐標(biāo)等
        var x = 0;
        var y = 0;
        var l = 0;
        var t = 0;
        var isDown = false;

        init();
        function init() {
            // 獲取錨點(diǎn)
            start = document.querySelector('#start')
            end = document.querySelector('#end')
            dot1 = document.querySelector('#dot1')
            dot2 = document.querySelector('#dot2')
            //錨點(diǎn)文字說(shuō)明以及跟隨樣式
            liArr = document.querySelectorAll('i');
            //獲取畫布
            c = document.getElementById("myCanvas");
            ctx = c.getContext("2d");
            // 創(chuàng)建漸變
            grd = ctx.createLinearGradient(0, 0, 512, 0);
            grd.addColorStop(0, "black");
            grd.addColorStop(1, "white");
            // 填充漸變
            ctx.fillStyle = grd;
            ctx.fillRect(0, 0, 512, 512);

            start.addEventListener('mousedown', downHandler)
            end.addEventListener('mousedown', downHandler)
            dot1.addEventListener('mousedown', downHandler)
            dot2.addEventListener('mousedown', downHandler)

            start.addEventListener('mouseup', upHandler)
            end.addEventListener('mouseup', upHandler)
            dot1.addEventListener('mouseup', upHandler)
            dot2.addEventListener('mouseup', upHandler)

            ctx.beginPath();
            drawLine()
            ctx.stroke();
        }
        //鼠標(biāo)按下回調(diào)
        function downHandler(e) {
            e.stopPropagation();
            // 獲取x坐標(biāo)和y坐標(biāo)
            x = e.clientX;
            y = e.clientY;
            //獲取左部和頂部的偏移量
            l = e.target.offsetLeft;
            t = e.target.offsetTop;
            //開(kāi)關(guān)打開(kāi)
            isDown = true;
            //設(shè)置樣式
            e.target.style.cursor = 'move';
            //添加移動(dòng)偵聽(tīng)
            e.target.addEventListener('mousemove', moveHandler)
            e.target.addEventListener('mouseup', moveHandler)
        }
        //拖拽回調(diào)
        function moveHandler(e) {
            if (isDown == false) {
                return;
            }
            //獲取x和y
            var nx = e.clientX;
            var ny = e.clientY;
            //計(jì)算移動(dòng)后的左偏移量和頂部的偏移量
            var nl = nx - (x - l);
            var nt = ny - (y - t);

            e.target.style.left = nl + 'px';
            e.target.style.top = nt + 'px';

            ctx.beginPath();
            drawLine();
            ctx.stroke();
        }

        function upHandler(e) {
            //注銷移動(dòng)偵聽(tīng)事件
            e.target.removeEventListener('mousemove', moveHandler)
        }
        //繪制貝塞爾曲線 /  設(shè)置 文字  /  背景反色
        function drawLine() {
            //獲取錨點(diǎn)坐標(biāo)
            let s1 = setNum(start.offsetLeft);
            let s2 = setNum(start.offsetTop);
            let e1 = setNum(end.offsetLeft);
            let e2 = setNum(end.offsetTop);
            let dd1 = setNum(dot1.offsetLeft);
            let dd2 = setNum(dot1.offsetTop);
            let dd3 = setNum(dot2.offsetLeft);
            let dd4 = setNum(dot2.offsetTop);

            ctx.clearRect(20, 20, 100, 50);
            ctx.fillStyle = grd;
            ctx.fillRect(0, 0, 512, 512);

            ctx.moveTo(s1, s2)
            ctx.bezierCurveTo(dd1, dd2, dd3, dd4, e1, e2);
            //顯示更新錨點(diǎn)坐標(biāo)
            liArr[0].innerText = `x=> ${s1} x=> ${s2}`;
            liArr[1].innerText = `x=> ${dd1} x=> ${dd2}`;
            liArr[2].innerText = `x=> ${dd3} x=> ${dd4}`
            liArr[3].innerText = `x=> ${e1} x=> ${e2}`

            //設(shè)置邊框顏色
            setColor(start, s1, s2);
            setColor(end, e1, e2);
            setColor(dot1, dd1, dd2);
            setColor(dot2, dd3, dd4);
        }
        //處理貝塞爾曲線坐標(biāo)點(diǎn), 與div 中心重合
        function setNum(str) {
            return parseInt(str) + 30
        }
        //設(shè)置背景色反色
        function setColor(target, x, y) {
            let col = ctx.getImageData(x, y, 1, 1).data;
            for (let i = 0; i < col.length; i++) {
                col[i] = 255 - col[i]
            }
            console.log(target, col);
            target.style.border = `20px solid rgb(${col[0]},${col[1]},${col[2]})`
        }
    </script>
</body>

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末诚卸,一起剝皮案震驚了整個(gè)濱河市葵第,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌合溺,老刑警劉巖卒密,帶你破解...
    沈念sama閱讀 222,590評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異棠赛,居然都是意外死亡哮奇,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,157評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門睛约,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)鼎俘,“玉大人,你說(shuō)我怎么就攤上這事辩涝∶撤ィ” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 169,301評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵怔揩,是天一觀的道長(zhǎng)捉邢。 經(jīng)常有香客問(wèn)我脯丝,道長(zhǎng),這世上最難降的妖魔是什么歌逢? 我笑而不...
    開(kāi)封第一講書人閱讀 60,078評(píng)論 1 300
  • 正文 為了忘掉前任巾钉,我火速辦了婚禮,結(jié)果婚禮上秘案,老公的妹妹穿的比我還像新娘砰苍。我一直安慰自己,他們只是感情好阱高,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,082評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布赚导。 她就那樣靜靜地躺著,像睡著了一般赤惊。 火紅的嫁衣襯著肌膚如雪吼旧。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 52,682評(píng)論 1 312
  • 那天未舟,我揣著相機(jī)與錄音圈暗,去河邊找鬼。 笑死裕膀,一個(gè)胖子當(dāng)著我的面吹牛员串,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播昼扛,決...
    沈念sama閱讀 41,155評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼寸齐,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了抄谐?” 一聲冷哼從身側(cè)響起渺鹦,我...
    開(kāi)封第一講書人閱讀 40,098評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蛹含,沒(méi)想到半個(gè)月后毅厚,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,638評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡浦箱,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,701評(píng)論 3 342
  • 正文 我和宋清朗相戀三年卧斟,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片憎茂。...
    茶點(diǎn)故事閱讀 40,852評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡珍语,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出竖幔,到底是詐尸還是另有隱情板乙,我是刑警寧澤,帶...
    沈念sama閱讀 36,520評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站募逞,受9級(jí)特大地震影響蛋铆,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜放接,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,181評(píng)論 3 335
  • 文/蒙蒙 一刺啦、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧纠脾,春花似錦玛瘸、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,674評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至慧脱,卻和暖如春渺绒,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背菱鸥。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,788評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工宗兼, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人氮采。 一個(gè)月前我還...
    沈念sama閱讀 49,279評(píng)論 3 379
  • 正文 我出身青樓殷绍,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親扳抽。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,851評(píng)論 2 361

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