最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

JavaScript+Canvas模擬實(shí)現(xiàn)支付寶畫年兔游戲

 更新時(shí)間:2023年01月14日 11:19:05   作者:鄭丫頭  
接近過年了,支付寶的集福的活動(dòng)又開始了,集美們的五福集齊了沒有。每年的集?;顒?dòng)都有一些小游戲,今年也不例外,畫年畫就是其中之一,本篇用canvas來寫一個(gè)畫年兔的游戲

接近過年了,支付寶的集福的活動(dòng)又開始了,集美們的五福集齊了沒有。每年的集?;顒?dòng)都有一些小游戲,今年也不例外,畫年畫就是其中之一,本篇用canvas來寫一個(gè)畫年兔的游戲。

動(dòng)手前的思路

畫年畫游戲規(guī)則是:跟著特定輪廓畫出線條來。

思考1、如何讓鼠標(biāo)只能在特定區(qū)域內(nèi)畫畫?

首先要獲取到這個(gè)輪廓區(qū)域所在畫布上的位置,判斷鼠標(biāo)繪畫的位置是否在指定范圍內(nèi)。用canvas的getImageData函數(shù)能夠獲取到畫布上有顏色的像素點(diǎn),然后根據(jù)像素點(diǎn)分布計(jì)算出像素點(diǎn)所在位置。

getImageData函數(shù)的用法在我之前的文章canvas文字粒子特效中有詳細(xì)介紹,不懂的可以去看看。

getImageData(canvas, ctx) {
    const data = ctx.getImageData(
        0,
        0,
        canvas.width,
        canvas.height,
    ).data;
    const gap = 4;
    const points = [];
    const length = data.length;
    for (let i = 0, wl = canvas.width * gap; i < length; i += gap) {
        if (data[i + gap - 1] == 255) {
            // 根據(jù)透明度判斷
            const x = (i % wl) / gap;
            const y = Math.ceil(i / wl);
            points.push([x, y]);
        }
    }
    return points;
}

思考2、如何讓繪制的圖畫動(dòng)起來

通過定時(shí)旋轉(zhuǎn)畫布實(shí)現(xiàn)。我選擇用幀動(dòng)畫requestAnimationFrame函數(shù),比setInterval函數(shù)性能更好一點(diǎn)。

思考3、如何撤銷上一步操作

將每一步繪制的點(diǎn)都記錄到創(chuàng)建的棧中,每一次撤銷都把上一步的繪制點(diǎn)刪除。

思考4、如何判斷線條繪制完畢

想了很久沒有什么太好的辦法,如果你有想法可以分享給我。當(dāng)mouseup事件執(zhí)行,會(huì)判斷當(dāng)前步驟下繪制的點(diǎn)數(shù)是否>=30,如果滿足條件會(huì)延遲半秒執(zhí)行下一步繪制,當(dāng)mousedown在半秒內(nèi)觸發(fā),延遲函數(shù)會(huì)取消,等待下一個(gè)mouseup事件。

// mouseup事件
const length = _this.execStack.reduce((prev, next) => {
    if (next.step == _this.curStep) {
        prev += next.points.length;
    }
    return prev;
}, 0)
if (length >= 30) {
    _this.timer = setTimeout(() => {
        if (_this.curStep == 2) {
            _this.curStep = 2.5
            _this.canEdit = false;
            _this.animate('ears_1', 3);
        } else if (_this.curStep == 4) {
            _this.curStep = 4.5
            _this.canEdit = false;
            _this.animate('shake_head', 5);
        } else if (_this.curStep == 6) {
            _this.curStep = 6.5
            _this.canEdit = false;
            _this.animate('shake_body', 7);
        } else if (_this.curStep <= 6) {
            _this.canEdit = true;
            _this.curStep += 1;
        }
        _this.execCanvas();
    }, 500);
}

關(guān)鍵步驟

1、創(chuàng)建一個(gè)RabbitPainting類,初始化時(shí)監(jiān)聽canvas的鼠標(biāo)點(diǎn)擊事件。要注意的是移動(dòng)端和pc端監(jiān)聽的事件不同:

this.pcEvents = ['mousedown', 'mousemove', 'mouseup'];
this.mobileEvents = ['touchstart', 'touchmove', 'touchend'];

我的代碼里對(duì)兼容的處理比較粗糙,只是將大致功能做出來了,所以大家多看看思路。

鼠標(biāo)移開之后,需要解除事件監(jiān)聽,當(dāng)鼠標(biāo)重新按壓時(shí)再綁定事件。mousedown事件監(jiān)聽流程如下:

鼠標(biāo)移動(dòng)時(shí),會(huì)得到兩個(gè)點(diǎn),鼠標(biāo)按壓位置和鼠標(biāo)移動(dòng)位置,如果繪制的線都是從按壓點(diǎn)到移動(dòng)點(diǎn)的話,就會(huì)畫出:

上圖所示,紅色的線是鼠標(biāo)移動(dòng)路徑,黑色的線是canvas畫出的線條,所以mousemove函數(shù)執(zhí)行后要更新初始按壓點(diǎn),使前后兩個(gè)點(diǎn)銜接在一起。

線條繪制函數(shù)如下:

drawLine(point) {
    const { ctx } = this
    ctx.beginPath()
    ctx.moveTo(point.startX, point.startY);
    ctx.lineTo(point.endX, point.endY);
    if (point.style) {
        for (let key in point.style) {
            ctx[key] = point.style[key]
        }
    }
    ctx.stroke();
    ctx.closePath()
}

2、兔子輪廓繪制,采用貝塞爾2階函數(shù)繪制圖形

// 外輪廓樣式
const wrapperStyle = {
    lineWidth: "30",
    strokeStyle: this.tipPathColor[0]
}
// 內(nèi)虛線樣式
const innerStyle = {
    lineWidth: "3",
    strokeStyle: this.tipPathColor[1],
    lineDash: [15, 12]
}
drawCurve({ list, wrapperStyle, innerStyle }) {
    const { tempCtx: ctx } = this
    list.forEach(point => {
        const { x, y, list } = point;
        ctx.beginPath();
        ctx.moveTo(x, y);
        ctx.bezierCurveTo(...list);
        for (let key in wrapperStyle) {
            ctx[key] = wrapperStyle[key]
        }
        ctx.stroke();
        ctx.save()
        ctx.beginPath();
        ctx.moveTo(x, y);
        ctx.bezierCurveTo(...list);
        for (let key in innerStyle) {
            if (key == 'lineDash') {
                ctx.setLineDash(innerStyle[key]);
            }
            ctx[key] = innerStyle[key]
        }
        ctx.stroke();
        ctx.restore();
    })
}

貝塞爾曲線的關(guān)鍵在于設(shè)置p1和p2兩個(gè)控制點(diǎn),大家自行把握。

我的兔子輪廓總體是這樣的:

3、旋轉(zhuǎn)畫布功能

使用canvas的rotate函數(shù),畫布的默認(rèn)中心點(diǎn)是(0,0),所以旋轉(zhuǎn)時(shí)需要用translate(x,y)函數(shù)將中心點(diǎn)移動(dòng)到特定位置。要注意旋轉(zhuǎn)后將畫布的中心點(diǎn)還原到(0,0)。

const rotateCanvas = (centerPoints, item) => {
    ctx.save()
    ctx.translate(...centerPoints)
    ctx.rotate(Math.PI / 180 * item.curDeg)
    ctx.translate(-centerPoints[0], -centerPoints[1])
}

注意ctx.save(),用來記錄畫布旋轉(zhuǎn)之前的狀態(tài),繪制結(jié)束后需要用ctx.restore()將畫布狀態(tài)還原,否則定時(shí)函數(shù)執(zhí)行角度旋轉(zhuǎn)時(shí)角度會(huì)累加。

4、眨眼睛動(dòng)畫

眨眼睛是用一張精靈圖,因?yàn)閳D片是我自己畫的,只有六幀,所以動(dòng)畫看起來不是很好,將就看著吧。

以上就是JavaScript+Canvas模擬實(shí)現(xiàn)支付寶畫年兔游戲的詳細(xì)內(nèi)容,更多關(guān)于JavaScript Canvas畫年兔游戲的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • JavaScript中的函數(shù)式編程詳解

    JavaScript中的函數(shù)式編程詳解

    這篇文章主要介紹了JavaScript中的函數(shù)式編程詳解,需要的朋友可以參考下
    2020-08-08
  • uniapp跨頁面?zhèn)髦祏ni.$emit和uni.$on的使用及踩坑實(shí)戰(zhàn)

    uniapp跨頁面?zhèn)髦祏ni.$emit和uni.$on的使用及踩坑實(shí)戰(zhàn)

    使用uni.$emit觸發(fā)事件后,對(duì)應(yīng)的uni.$on就會(huì)監(jiān)聽到事件觸發(fā),在回調(diào)中去執(zhí)行相關(guān)的邏輯,下面這篇文章主要給大家介紹了關(guān)于uniapp跨頁面?zhèn)髦祏ni.$emit和uni.$on的使用及踩坑實(shí)戰(zhàn)的相關(guān)資料,需要的朋友可以參考下
    2023-04-04
  • JavaScript中如何判斷一個(gè)值是否為Null

    JavaScript中如何判斷一個(gè)值是否為Null

    我們?cè)陂_發(fā)的時(shí)候經(jīng)常會(huì)判斷一個(gè)null值,那么我們?cè)撊绾稳ヅ袛嗄?下面這篇文章主要給大家介紹了關(guān)于JavaScript中如何判斷一個(gè)值是否為Null的相關(guān)資料,需要的朋友可以參考下
    2023-12-12
  • Layui選項(xiàng)卡制作歷史瀏覽記錄的方法

    Layui選項(xiàng)卡制作歷史瀏覽記錄的方法

    今天小編大家分享一篇Layui選項(xiàng)卡制作歷史瀏覽記錄的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2019-09-09
  • JavaScript生成GUID的多種算法小結(jié)

    JavaScript生成GUID的多種算法小結(jié)

    這篇文章介紹了GUID的幾種生成算法,有需要的朋友可以參考一下
    2013-08-08
  • 如何去除js中的json存在的轉(zhuǎn)義字符\問題

    如何去除js中的json存在的轉(zhuǎn)義字符\問題

    這篇文章主要介紹了如何去除js中的json存在的轉(zhuǎn)義字符\問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-09-09
  • js變量以及其作用域詳解

    js變量以及其作用域詳解

    Javascript和Java、C這些語言不同,它是一種無類型、弱檢測(cè)的語言。它對(duì)變量的定義并不需要聲明變量類型,我們只要通過賦值的形式,可以將各種類型的數(shù)據(jù)賦值給同一個(gè)變量
    2012-01-01
  • 詳解JavaScript中的Object.is()與

    詳解JavaScript中的Object.is()與"==="運(yùn)算符總結(jié)

    這篇文章主要介紹了詳解JavaScript中的Object.is()與"==="運(yùn)算符總結(jié),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-06-06
  • 輕松學(xué)習(xí)Javascript閉包

    輕松學(xué)習(xí)Javascript閉包

    閉包(closure)是Javascript語言的一個(gè)難點(diǎn),也是它的特色,很多高級(jí)應(yīng)用都要依靠閉包實(shí)現(xiàn)。這篇文章主要介紹了Javascript閉包,需要的朋友可以參考下
    2017-03-03
  • js實(shí)現(xiàn)百度地圖定位于地址逆解析,顯示自己當(dāng)前的地理位置

    js實(shí)現(xiàn)百度地圖定位于地址逆解析,顯示自己當(dāng)前的地理位置

    本文分享了基于javascript實(shí)現(xiàn)的百度地圖定位于地址逆解析,顯示自己當(dāng)前的地理位置的實(shí)例代碼,有興趣的朋友可以看下
    2016-12-12

最新評(píng)論

汝阳县| 固安县| 高密市| 永胜县| 和平区| 宁化县| 苏尼特右旗| 龙陵县| 富锦市| 呼玛县| 漳平市| 沐川县| 玛沁县| 府谷县| 马关县| 临高县| 黄平县| 永顺县| 茂名市| 息烽县| 成都市| 宣汉县| 綦江县| 中山市| 柳州市| 咸阳市| 博客| 贡嘎县| 桓台县| 策勒县| 陇川县| 义马市| 松桃| 兰州市| 五常市| 南澳县| 武安市| 宜阳县| 荔波县| 西安市| 托克逊县|