国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

canvas中的拖拽、縮放、旋轉(zhuǎn) (下) —— 代碼實(shí)現(xiàn)

pumpkin9 / 1128人閱讀

摘要:中的拖拽縮放旋轉(zhuǎn)上數(shù)學(xué)知識(shí)準(zhǔn)備。表示整個(gè)區(qū)域,表示中的元素。事實(shí)上,工作上的需求并沒有要求旋轉(zhuǎn),只需要實(shí)現(xiàn)拖拽縮放即可。

寫在前面

本文首發(fā)于公眾號(hào):符合預(yù)期的CoyPan

demo體驗(yàn)地址及代碼在這里:請(qǐng)用手機(jī)或?yàn)g覽器模擬手機(jī)訪問(wèn)

上一篇文章介紹了canvas中的拖拽、縮放、旋轉(zhuǎn)中涉及到的數(shù)學(xué)知識(shí)。可以點(diǎn)擊下面的鏈接查看。

canvas中的拖拽、縮放、旋轉(zhuǎn) (上) —— 數(shù)學(xué)知識(shí)準(zhǔn)備。

代碼準(zhǔn)備 - 如何在canvas中畫出一個(gè)帶旋轉(zhuǎn)角度的元素

canvas中,如果一個(gè)元素帶有一個(gè)旋轉(zhuǎn)角度,可以直接變化canvas的坐標(biāo)軸來(lái)畫出此元素。舉個(gè)例子,

ctx.save(); // 保存舊的坐標(biāo)系狀態(tài)
ctx.translate(x0 + w / 2, y0 + h / 2); // 坐標(biāo)原點(diǎn)移動(dòng)到旋轉(zhuǎn)中心
ctx.rotate(angle); // 旋轉(zhuǎn)坐標(biāo)系
ctx.translate(-(x0 + w / 2),  -(y0 + h / 2)); // 坐標(biāo)原點(diǎn)還原
ctx.rect(x0, y0, w, h); // 以新坐標(biāo)系為參照,畫出矩形。
ctx.restore(); // 還原之前的坐標(biāo)系狀態(tài)
代碼整體思路

整個(gè)demo的實(shí)現(xiàn)思路如下:

用戶開始觸摸(touchstart)時(shí),獲取用戶的觸摸對(duì)象,是Sprite的本體?刪除按鈕?縮放按鈕?旋轉(zhuǎn)按鈕?并且根據(jù)各種情況,對(duì)變化參數(shù)進(jìn)行初始化。

用戶移動(dòng)手指(touchmove)時(shí),根據(jù)手指的坐標(biāo),更新stage中的所有元素的位置、大小,記錄變化參數(shù)。修改對(duì)應(yīng)sprite的屬性值。同時(shí)對(duì)canvas進(jìn)行重繪。

用戶一旦停止觸摸(touchend)時(shí),根據(jù)變化參數(shù),更新sprite的坐標(biāo),同時(shí)對(duì)變化參數(shù)進(jìn)行重置。

需要注意的是,在touchmove的過(guò)程中,并不需要更新sprite的坐標(biāo),只需要記錄變化的參數(shù)即可。在touchend過(guò)程中,再進(jìn)行坐標(biāo)的更新。坐標(biāo)的唯一用處,就是判斷用戶點(diǎn)擊時(shí),落點(diǎn)是否在指定區(qū)域內(nèi)。

代碼細(xì)節(jié)

首先,聲明兩個(gè)類:StageSpriteStage表示整個(gè)canvas區(qū)域,Sprite表示canvas中的元素。我們可以在Stage中添加多個(gè)Sprite,刪除Sprite。這兩個(gè)類的屬性如下。

class Stage {
    constructor(props) {
        
        this.canvas = props.canvas;
        this.ctx = this.canvas.getContext("2d");
        
        // 用一個(gè)數(shù)組來(lái)保存canvas中的元素。每一個(gè)元素都是一個(gè)Sprite類的實(shí)例。
        this.spriteList = []; 

        // 獲取canvas在視窗中的位置,以便計(jì)算用戶touch時(shí),相對(duì)與canvas內(nèi)部的坐標(biāo)。
        const pos = this.canvas.getBoundingClientRect(); 
        this.canvasOffsetLeft = pos.left;
        this.canvasOffsetTop = pos.top;

        this.dragSpriteTarget = null; // 拖拽的對(duì)象
        this.scaleSpriteTarget = null; // 縮放的對(duì)象
        this.rotateSpriteTarget = null; // 旋轉(zhuǎn)的對(duì)象

        this.dragStartX = undefined; 
        this.dragStartY = undefined;
        this.scaleStartX = undefined;
        this.scaleStartY = undefined;
        this.rotateStartX = undefined;
        this.rotateStartY = undefined;

    }
}

class Sprite {
    constructor(props) {
        
        // 每一個(gè)sprite都有一個(gè)唯一的id
        this.id = Date.now() + Math.floor(Math.random() * 10);
        
        this.pos = props.pos; // 在canvas中的位置
        this.size = props.size; // sprite的當(dāng)前大小
        this.baseSize = props.size; // sprite的初始化大小
        this.minSize = props.minSize; // sprite縮放時(shí)允許的最小size
        this.maxSize = props.maxSize; // sprite縮放時(shí)允許的最大size
        
        // 中心點(diǎn)坐標(biāo)
        this.center = [
            props.pos[0] + props.size[0] / 2, 
            props.pos[1] + props.size[1] / 2
        ];
        
        this.delIcon = null;
        this.scaleIcon = null;
        this.rotateIcon = null;

        // 四個(gè)頂點(diǎn)的坐標(biāo),順序?yàn)椋鹤笊希疑希笙拢蚁?        this.coordinate = this.setCoordinate(this.pos, this.size); 

        this.rotateAngle = 0; // 累計(jì)旋轉(zhuǎn)的角度
        this.rotateAngleDir = 0; // 每次旋轉(zhuǎn)角度

        this.scalePercent = 1; // 縮放比例
        
    }
}

demo中,點(diǎn)擊canvas下方的紅色方塊時(shí),會(huì)實(shí)例化一個(gè)sprite,調(diào)用stage.append時(shí),會(huì)將實(shí)例化的sprite直接push到StagespriteList屬性內(nèi)。

window.onload = function () {

    const stage = new Stage({
        canvas: document.querySelector("canvas")
    });

    document.querySelector(".red-box").addEventListener("click", function () {
        const randomX = Math.floor(Math.random() * 200);
        const randomY = Math.floor(Math.random() * 200);
        const sprite = new Sprite({
            pos: [randomX, randomY],
            size: [120, 60],
            minSize: [40, 20],
            maxSize: [240, 120]
        });
        stage.append(sprite);
    });
}

下面是Stage的方法:

class Stage {

    constructor(props) {}

    // 將sprite添加到stage內(nèi)
    append(sprite) {}

    // 監(jiān)聽事件
    initEvent() {}

    // 處理touchstart
    handleTouchStart(e) {}

    // 處理touchmove
    handleTouchMove(e) {}

    // 處理touchend
    handleTouchEnd() {}

    // 初始化sprite的拖拽事件
    initDragEvent(sprite, { touchX, touchY }) {}

    // 初始化sprite的縮放事件
    initScaleEvent(sprite, { touchX, touchY }) {}

    // 初始化sprite的旋轉(zhuǎn)事件
    initRotateEvent(sprite, { touchX, touchY }) {}

    // 通過(guò)觸摸的坐標(biāo)重新計(jì)算sprite的坐標(biāo)
    reCalSpritePos(sprite, touchX, touchY) {}

    // 通過(guò)觸摸的【橫】坐標(biāo)重新計(jì)算sprite的大小
    reCalSpriteSize(sprite, touchX, touchY) {}

    // 重新計(jì)算sprite的角度
    reCalSpriteRotate(sprite, touchX, touchY) {}

    // 返回當(dāng)前touch的sprite
    getTouchSpriteTarget({ touchX, touchY }) {}

    // 判斷是否touch在了sprite中的某一部分上,返回這個(gè)sprite
    getTouchTargetOfSprite({ touchX, touchY }, part) {}

    // 返回觸摸點(diǎn)相對(duì)于canvas的坐標(biāo)
    normalizeTouchEvent(e) {}

    // 判斷是否在在某個(gè)sprite中移動(dòng)。當(dāng)前默認(rèn)所有的sprite都是長(zhǎng)方形的。
    checkIfTouchIn({ touchX, touchY }, sprite) {}

    // 從場(chǎng)景中刪除
    remove(sprite) {}

    // 畫出stage中的所有sprite
    drawSprite() {}

    // 清空畫布
    clearStage() {}
}

Sprite的方法:

class Sprite {

    constructor(props) {}

    // 設(shè)置四個(gè)頂點(diǎn)的初始化坐標(biāo)
    setCoordinate(pos, size) {}
    
    // 根據(jù)旋轉(zhuǎn)角度更新sprite的所有部分的頂點(diǎn)坐標(biāo)
    updateCoordinateByRotate() {}
    
    // 根據(jù)旋轉(zhuǎn)角度更新頂點(diǎn)坐標(biāo)
    updateItemCoordinateByRotate(target, center, angle){}

    // 根據(jù)縮放比例更新頂點(diǎn)坐標(biāo)
    updateItemCoordinateByScale(sprite, center, scale) {}

    // 根據(jù)按鈕icon的頂點(diǎn)坐標(biāo)獲取icon中心點(diǎn)坐標(biāo)
    getIconCenter(iconCoordinate) {}

    // 根據(jù)按鈕icon的中心點(diǎn)坐標(biāo)獲取icon的頂點(diǎn)坐標(biāo)
    getIconCoordinateByIconCenter(center) {}

    // 根據(jù)縮放比更新頂點(diǎn)坐標(biāo)
    updateCoordinateByScale() {}

    // 畫出該sprite
    draw(ctx) {}

    // 畫出該sprite對(duì)應(yīng)的按鈕icon
    drawIcon(ctx, icon) {}

    // 對(duì)sprite進(jìn)行初始化
    init() {}

    // 初始化刪除按鈕,左下角
    initDelIcon() {}

    // 初始化縮放按鈕,右上角
    initScaleIcon() {}

    // 初始化旋轉(zhuǎn)按鈕,左上角
    initRotateIcon() {}

    // 重置icon的位置與大小
    resetIconPos() {}

    // 根據(jù)移動(dòng)的距離重置sprite所有部分的位置
    resetPos(dirX, dirY) {}

    // 根據(jù)觸摸點(diǎn)移動(dòng)的距離計(jì)算縮放比,并重置sprite的尺寸
    resetSize(dir) {}

    // 設(shè)置sprite的旋轉(zhuǎn)角度
    setRotateAngle(angleDir) {}
}

Stage的方法主要是處理和用戶交互的邏輯,得到用戶操作的交互參數(shù),然后根據(jù)交互參數(shù)調(diào)用Sprite的方法來(lái)進(jìn)行變化。

代碼在這里:https://coypan.info/demo/canvas-drag-scale-rotate.html

寫在后面

本文介紹了文章開頭給出的demo的詳細(xì)實(shí)現(xiàn)過(guò)程。代碼還有很大的優(yōu)化空間。事實(shí)上,工作上的需求并沒有要求【旋轉(zhuǎn)】,只需要實(shí)現(xiàn)【拖拽】、【縮放】即可。在只實(shí)現(xiàn)【拖拽】和【縮放】的情況下,會(huì)容易很多,不需要用到四個(gè)頂點(diǎn)的坐標(biāo)以及之前的那些復(fù)雜的數(shù)學(xué)知識(shí)。而在自己實(shí)現(xiàn)【旋轉(zhuǎn)】的過(guò)程中,也學(xué)到了很多。符合預(yù)期。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/101699.html

相關(guān)文章

  • 基于javascript的拖拽類封裝

    摘要:參考了很多別人寫的代碼,最后終于弄明白了其中的原理,自己也寫了一個(gè)。效果圖如下地址如下拖拽類封裝代碼使用方法引入和對(duì)應(yīng)的。如果沒有為的結(jié)構(gòu),就創(chuàng)建。鼠標(biāo)移動(dòng)時(shí),記錄再次計(jì)算鼠標(biāo)位置距離中心位置的的反正切函數(shù)。 在公司做一個(gè)h5編輯平臺(tái),中間需要對(duì)元素進(jìn)行拖拽、放大縮小、旋轉(zhuǎn)等操作,且需要對(duì)文本、圖片、音樂(lè)組件等不同元素都可以具備這些功能。參考了很多別人寫的代碼,最后終于弄明白了其中的原...

    afishhhhh 評(píng)論0 收藏0
  • 用typescript開發(fā)手勢(shì)庫(kù) - (1)web開發(fā)常用手勢(shì)有哪些?

    這只是個(gè)開頭 說(shuō)在最前面,本文是一個(gè)系列文章的開頭, 這個(gè)系列里我會(huì)講如何用typescript開發(fā)一款支持pc和手機(jī)端的手勢(shì)庫(kù)any-touch, 以及通過(guò)jest讓你的代碼測(cè)試覆蓋率100%. showImg(https://segmentfault.com/img/bVbp3B0?w=936&h=246); 目錄 用TypeScript開發(fā)手勢(shì)庫(kù) - (2)tsconfig.json & r...

    raise_yang 評(píng)論0 收藏0
  • 多功能React影像組件(拖拽、水印、縮放、切換、旋轉(zhuǎn))

    摘要:移動(dòng)的過(guò)程中可以通過(guò)拿到元素的坐標(biāo),記為。向上滾動(dòng)放大,向下滾動(dòng)縮小這里要注意控制最小縮放值。還要注意的是圖片在邊界的縮放,不然圖片可能會(huì)移動(dòng)在屏幕外。代碼實(shí)現(xiàn)控制滾輪縮放計(jì)算縮放后的大小每一次滾輪限制最小不讓由于縮小消失在視野中 cxj-react-image 用法如下: yarn add cxj-react-image // npm i cxj-react-image import...

    soasme 評(píng)論0 收藏0
  • jQuery 圖片查看插件 Magnify 開發(fā)簡(jiǎn)介(仿 Windows 照片查看器)

    摘要:隨后會(huì)陸續(xù)發(fā)布及相關(guān)版本的插件。這和圖片查看器的操作方式是相同的。目前的調(diào)整大小存在一點(diǎn),但不影響整體的使用。鍵盤控制和照片查看器的按鍵是一樣的。除了照片查看器,的圖片查看器也非常的高大上。 showImg(https://segmentfault.com/img/remote/1460000012565638?w=750&h=375); 前言 因?yàn)橐恍┨厥獾臉I(yè)務(wù)需求,經(jīng)過(guò)一個(gè)多月的蟄...

    anyway 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

pumpkin9

|高級(jí)講師

TA的文章

閱讀更多
最新活動(dòng)
閱讀需要支付1元查看
<