拖拽一個元素如此簡單,mouse、drag、touch三兄弟的用處

一生無過發表於2020-12-30

最近需要做一個投票活動,上傳圖片時需要拖拽、縮放來裁剪圖片,vue的元件不少,不過自己動手才能豐衣足食,一味使用別人的元件實在難以進步,所以自己研究一番。

一、mouse、drag、touch傻傻分不清

  1. mouse:pc端的滑鼠按下、移動等事件 
    (1)、mousedown:
    當滑鼠指標移動到元素上方,並按下滑鼠按鍵時,會發生 mousedown 事件。
            與 click 事件不同,mousedown 事件僅需要按鍵被按下,而不需要鬆開即可發生。mousedown() 方法觸發 mousedown 事件,或規定當發生 mousedown 事件 時執行的函式。
    (2)、mouseenter:當滑鼠指標穿過元素時,會發生 mouseenter 事件。
            該事件大多數時候會與mouseleave事件一起使用。mouseenter() 方法觸發 mouseenter 事件,或規定當發生 mouseenter 事件時執行的函式。(與 mouseover 事件不同,只有在滑鼠指標穿過被選元素            時,才會觸發 mouseenter 事件。如果滑鼠指標穿過任何子元素,同樣會觸發 mouseover 事件。)
    (3)、mouseleave:當滑鼠指標離開元素時,會發生 mouseleave 事件。該事件大多數時候會與mouseenter事件一起使用。
            mouseleave() 方法觸發 mouseleave 事件,或規定當發生 mouseleave 事件時執行的函式。(與 mouseout 事件不同,只有在滑鼠指標離開被選元素時,才會觸發 mouseleave 事件。如果滑鼠指標離開          任何子元素,同樣會觸發 mouseout 事件。請看下面例子的演示。)
    (4)、mousemove:當滑鼠指標在指定的元素中移動時,就會發生 mousemove 事件。
             mousemove() 方法觸發 mousemove 事件,或規定當發生 mousemove 事件時執行的函式。(注意:使用者把滑鼠移動一個畫素,就會發生一次 mousemove 事件。處理所有 mousemove 事件會耗費系            統資源。請謹慎使用該事件。)
    (5)、mouseout:當滑鼠指標從元素上移開時,發生 mouseout 事件。該事件大多數時候會與mouseover事件一起使用。
            mouseout() 方法觸發 mouseout 事件,或規定當發生 mouseout 事件時執行的函式。(與 mouseleave 事件不同,不論滑鼠指標離開被選元素還是任何子元素,都會觸發 mouseout 事件。只有在滑鼠指          針離開被選元素時,才會觸發 mouseleave 事件。)
    (6)、mouseover:當滑鼠指標位於元素上方時,會發生 mouseover 事件。該事件大多數時候會與mouseout事件一起使用。
            mouseover() 方法觸發 mouseover 事件,或規定當發生 mouseover 事件時執行的函式。(與 mouseenter 事件不同,不論滑鼠指標穿過被選元素或其子元素,都會觸發 mouseover 事件。只有在滑鼠指          針穿過被選元素時,才會觸發 mouseenter 事件。)
    (7)、mouseup:當在元素上放鬆滑鼠按鈕時,會發生 mouseup 事件。與 click 事件不同,mouseup 事件僅需要放鬆按鈕。當滑鼠指標位於元素上方時,放鬆滑鼠按鈕就會觸發該事件。
            mouseup() 方法觸發 mouseup 事件,或規定當發生 mouseup 事件時執行的函式。
    <div @mousedown="gMousedown"
         @mouseenter="gMouseenter"
         @mouseleave="gMouseleave"
         @mousemove="gMousemove"
         @mouseout="gMouseout" 
         @mouseover="gMouseover" 
         @mouseup="gMouseup">
         試一試移動
    </div>
    
    methods:{
                gMousedown(e){
                    console.log("滑鼠左鍵按下了");
                    console.log(e)
                },
                gMouseenter(e){
                    console.log("滑鼠穿過了");
                    console.log(e)
                },
                gMouseleave(e){
                    console.log("滑鼠離開了");
                    console.log(e)
                },
                gMousemove(e){
                    console.log("滑鼠移動了");
                    console.log(e)
                },
                gMouseout(e){
                    console.log("滑鼠移開了");
                    console.log(e)
                },
                gMouseover(e){
                    console.log("滑鼠在元素上面了");
                    console.log(e)
                },
                gMouseup(e){
                    console.log("滑鼠鬆開了");
                    console.log(e)
                }
            }
  2. drag:pc端的滑鼠事件,滑鼠左鍵按下並且拖動
    (1)、在被拖動目標上觸發的事件:
      ondragstart:在使用者開始拖動元素或選擇的文字時觸發(為了讓元素可拖動,需要使用draggable屬性,連結和圖片預設是可拖動的,不需要 draggable 屬性)
      ondrag:元素正在拖動時觸發
           ondragend:使用者完成元素拖動後觸發
    (2)、在其他物件容器中觸發的事件:
           ondragenter:當被滑鼠拖動的物件進入其容器範圍內時觸發此事件
      ondragover:當某被拖動的物件在另一物件容器範圍內拖動時觸發此事件
           ondragleave:當被滑鼠拖動的物件離開其容器範圍內時觸發此事件
           ondrop:在一個拖動過程中,釋放滑鼠鍵時觸發此事件
    <template>
        <div style="margin-top: 66px;">
            <div class="drag-container">
                <p v-for="item in list"
                   draggable="true"
                   @dragstart="dragstart($event,item,'list')"
                   @dragend="dragend($event,item)"
                >{{item.name}}</p>
            </div>
            <div class="drop-container">
                <p>分層</p>
                <div class="drop-area" v-if="showLayer"
                     @drop="drop($event,'layer')"
                     @dragenter="dragenter"
                     @dragover="dragover"
                >
                    <p v-for="(item,index) in layer">
                        <span
                                draggable="true"
                                @dragstart="dragstart($event,item,'layer',index)"
                        >{{item.name}}</span>
                        <b v-if="index!==layer.length-1">-></b>
                    </p>
                </div>
                <p>維度</p>
                <div class="drop-area"
                     @drop="drop($event,'dimensions')"
                     @dragover="dragover">
                    <span v-for="(item,index) in dimensions"
                          @drop="dropToItem($event,item)"
                          @dragover.prevent
                          draggable="true"
                          @dragstart="dragstart($event,item,'dimensions',index)"
                    >{{item.name}}</span>
                </div>
                <p>對比</p>
                <div class="drop-area"
                     @drop="drop($event,'contrasts')"
                     @dragover="dragover">
                    <span v-for="(item,index) in contrasts"
                          @dragover.prevent
                          draggable="true"
                          @dragstart="dragstart($event,item,'contrasts',index)"
                    >{{item.name}}</span>
                </div>
            </div>
        </div>
    </template>
    
    <script>
        export default {
            data() {
                return {
                    dragItem: {},//拖動的欄位
                    dragFrom: '',//拖動從哪開始
                    dragIndex: '',//拖動的欄位在陣列中的索引
                    dropIndex: '',//放入地方的欄位的索引
                    showLayer: false,//是否顯示分層
                    isDropToItem: false,//是否是拖動到維度欄位中進行分層操作
                    layer: [],
                    dimensions: [],
                    contrasts: [],
                    list: [
                        {
                            id: 1,
                            name: '姓名'
                        }, {
                            id: 2,
                            name: '性別'
                        }, {
                            id: 3,
                            name: '年齡'
                        }, {
                            id: 4,
                            name: '分數'
                        }]
                }
            },
            methods: {
                //拖拽開始
                dragstart(event, item, frm, index) {
                    console.log('拖拽開始');
                    console.log(event);
                    console.log(item);
                    console.log(frm);
                    console.log(index);
                    const that = this;
                    event.dataTransfer.setData("Text", event.target.id);
                    that.dragItem = item;
                    that.dragFrom = frm;
                    if (!isNaN(index)) {
                        that.dragIndex = index;
                    }
                },
                //進入拖拽區域,進入時觸發一次
                dragenter(event) {
                    console.log('進入拖拽區域,進入時觸發一次');
                    console.log(event);
                },
                //進入拖拽區域後多次觸發
                dragover(event) {
                    console.log("進入拖拽區域後多次觸發");
                    console.log(event);
                    const that = this;
                    event.preventDefault();
                    let target = event.target;
                    that.dropIndex = that.indexFn(target);
                    let nodeName = target.nodeName;
                    if (nodeName !== 'SPAN') {
                        that.dropIndex = -1;
                    }
                },
                //鬆開滑鼠完成拖拽後觸發
                drop(event, target) {
                    console.log('鬆開滑鼠完成拖拽後觸發');
                    console.log(event);
                    console.log(target);
                    const that = this;
                    let dragFrom = that.dragFrom;
                    let dragItem = that.dragItem;
                    let dragIndex = that.dragIndex;
                    let dropIndex = that.dropIndex;
                    if (that.isDropToItem) {
                        return;
                    }
                    if (that.dragFrom === 'layer') {
                        that.layer.splice(dragIndex, 1);
                    } else if (that.dragFrom === 'dimensions') {
                        that.dimensions.splice(dragIndex, 1);
                    } else if (that.dragFrom === 'contrasts') {
                        that.contrasts.splice(dragIndex, 1);
                    }
                    if (dragFrom === target && dropIndex > -1) {
                        let targetArr = that[target];
                        targetArr.splice(dropIndex, 0, dragItem);
                        return;
                    }
    
                    if (target === 'layer') {
                        that.layer.push(dragItem);
                    } else if (target === 'dimensions') {
                        that.dimensions.push(dragItem);
                    } else if (target === 'contrasts') {
                        that.contrasts.push(dragItem);
                    }
                },
                //拖動到維度第一個欄位時觸發
                dropToItem(event, item) {
                    console.log('拖動到維度第一個欄位時觸發');
                    console.log(event);
                    console.log(item);
                    const that = this;
                    if (that.dragFrom !== 'list') {
                        that.isDropToItem = false;
                        return;
                    } else {
                        that.isDropToItem = true
                    }
                    if (that.showLayer) {
                        that.layer.push(this.dragItem);
                    } else {
                        that.showLayer = true;
                        that.layer.push(item);
                        that.layer.push(this.dragItem);
                    }
                },
                //拖拽結束後觸發,不管是否拖拽成功
                dragend(event, item) {
                    console.log('拖拽結束後觸發,不管是否拖拽成功');
                    console.log(event);
                    console.log(item);
                    const that = this;
                    that.isDropToItem = false
                },
                //判斷拖動欄位的所以,用於排序
                indexFn(el) {
                    let index = 0;
                    if (!el || !el.parentNode) {
                        return -1;
                    }
                    while (el && (el = el.previousElementSibling)) {
                        index++;
                    }
                    return index;
                },
            }
        }
    </script>
    <style scoped lang="less">
        /* css部分 */
        .drag-container {
            width: 66px;
            height: 500px;
            float: left;
            background-color: #BAB5F5;
            p {
                line-height: 40px;
                text-align: center;
                cursor: pointer;
            }
        }
        .drop-container {
            margin-left: 120px;
            float: left;
            height: 500px;
            width: 600px;
            p {
                color: #fff;
                line-height: 40px;
            }
            .drop-area {
                height: 80px;
                width: 600px;
                background-color: #4c72ff;
                padding: 10px;
                p {
                    display: inline-block;
                    margin: 0;
                }
                span {
                    display: inline-block;
                    min-width: 50px;
                    line-height: 40px;
                    margin-right: 5px;
                    background-color: #5a3e99;
                    text-align: center;
                    cursor: pointer;
                }
            }
        }
    </style>
  3. touch:移動端的觸控事件,觸發的條件是手指按在螢幕上並且移動(手指不能離開螢幕)
    (1)、touchstart:當手指觸控螢幕時觸發;即使已經有一個手指放在了螢幕上也會觸發。
    (2)、touchmove:當手指在螢幕上滑動時連續的觸發。在這個事件發生期間,呼叫preventDefault()可阻止滾動。
    (3)、touchend:當手指從螢幕上移開時觸發。
    (4)、touchcancel:當系統停止跟蹤觸控時觸發。關於此事件的確切觸發事件,文件中沒有明確說明。
    3.1、以上event物件都包含以下屬性:
    (1)、touches:表示當前跟蹤的觸控操作的Touch物件的陣列。
    (2)、targetTouches:特定於事件目標的Touch物件的陣列。
    (3)、changeTouches:表示自上次觸控以來發生了什麼改變的Touch物件的陣列。
    3.2、每個Touch物件包含下列屬性:
    (1)、clientX:觸控目標在視口中的X座標。
    (2)、clientY:觸控目標在視口中的Y座標。
    (3)、identifier:表示觸控的唯一ID。
    (4)、pageX:觸控目標在頁面中的x座標。
    (5)、pageY:觸控目標在頁面中的y座標。
    (6)、screenX:觸控目標在螢幕中的x座標。
    (7)、screenY:觸控目標在螢幕中的y座標。
    (8)、target:觸控的DOM節點座標

二、運用touch實現圖片手勢推拽、縮放裁剪圖片

  1. 通過touchstart和touchmove實現拖拽圖片:
    (1)、touchstart:獲取touches[0]的pageX,pageY來更新scxscy以及更新iXiY
    (2)、touchmovw:獲取touches[0]的pageX,宣告變數f1x存放,移動後的x座標等於iX + f1x - scx,y座標同理,最後呼叫_drawImage來更新圖片

  2. 通過event.touches的數量判斷是否是單指或雙指實現圖片縮放:
    (1)、縮放後圖片的寬高:圖片初始寬度*縮放倍率和圖片初始高度*縮放倍率
    (1)、縮放倍率:首先在touchstart事件上求取兩手指間的距離d1;然後在touchmove事件上繼續求取兩手指間的距離d2當前縮放倍率= 初始縮放倍率 + (d2-d1) / 步長
    (1)、縮放後圖片左上角的座標值:首先要找到一個縮放中心(先取雙指的中點座標,但是這個座標必須要位於圖片上,如果不在圖片上,則取圖片上離該中點座標最近的點),通過等式(縮放中心x座標 - 縮放後圖片左上角x座標)/ 縮放後圖片的寬度 = (縮放中心x座標 - 縮放前圖片左上角x座標)/ 縮放前圖片的寬度;(y座標同理)

  3. 通過canvas畫布擷取畫布中的圖片
    <template>
        <div class="clipper-container" ref="clipper">
            <canvas ref="canvas"></canvas>
            <!-- 裁剪部分 -->
            <div class="clipper-part">
                <div class="pCanvas-container">
                    <canvas ref="pCanvas"></canvas>
                </div>
            </div>
            <!-- 底部操作欄 -->
            <div class="action-bar">
                <button class="btn-cancel" @click="cancel">取消</button>
                <button class="btn-ok" @click="clipper">確認</button>
            </div>
            <!-- 背景遮罩 -->
            <div class="mask" :class="{opacity: maskShow}"></div>
            <!-- 手勢操作層 -->
            <div class="gesture-mask" ref="gesture" @touchstart="getTouchstart" @touchmove="getTouchmove" @touchend="getTouchend"></div>
        </div>
    </template>
    
    <style lang="less">
        .position() {
            position: absolute;
            top: 0;
            bottom: 0;
            left: 0;
            right: 0;
            z-index: 100;
        }
        .clipper-container {
            .position();
            line-height: 0;
            background-color: #000;
            .clipper-part {
                .position();
                bottom: 61px;
                z-index: 102;
                .pCanvas-container {
                    position: absolute;
                    top: 50%;
                    left: 50%;
                    transform: translate(-50%, -50%);
                    border: 2px solid #fff;
                }
            }
            .action-bar {
                box-sizing: content-box;
                .position();
                top: auto;
                z-index: 103;
                height: 60px;
                line-height: 60px;
                border-top: 1px solid rgba(256, 256, 256, 0.3);
                button {
                    display: block;
                    padding: 0 15px;
                    line-height: 60px;
                    font-size: 16px;
                    color: #fff;
                    background: none;
                    border: none;
                    outline: 0;
                    &.btn-cancel {
                        float: left;
                    }
                    &.btn-ok {
                        float: right;
                    }
                }
            }
            .mask {
                .position();
                z-index: 101;
                transition: opacity 500ms;
                background-color: #000;
                opacity: 0;
                &.opacity {
                    opacity: 0.8;
                }
            }
            .gesture-mask {
                .position();
                bottom: 61px;
                z-index: 103;
            }
        }
    </style>
    
    <script>
        export default {
            name: 'imageTailor',
            props: {
                img: String, //url或dataUrl
                clipperImgWidth: {
                    type: Number,
                    default: 500
                },
                clipperImgHeight: {
                    type: Number,
                    default: 200
                }
            },
            watch: {
                img() {
                    const that = this;
                    that.loadImgQueue.push(that.img);
                    that.loadImg();
                }
            },
            data() {
                return {
                    originXDiff: 0, //裁剪canvas與原圖canvas座標原點上的差值
                    originYDiff: 0,
                    maskShow: true,
                    maskShowTimer: null,
                    ctx: null,
                    pCtx: null,
                    actionBarHeight: 61,
                    loadImgQueue: [], //載入圖片佇列
                    imageData: null,
                    imgLoaded: false,
                    imgLoading: false,
                    imgStartWidth: null,
                    imgStartHeight: null,
                    imgCurrentWidth: null,
                    imgCurrentHeight: null,
                    imgX: null, //img對於canvas的座標
                    imgY: null,
                    imgScale: 1, //圖片目前的縮放倍數 範圍是1-5
                    imgMinScale: 1,
                    imgMaxScale: 5,
                    imgScaleStep: 60, //縮放步長,每60px加減0.1
                    //圖片canvas寬高
                    cWidth: 0,
                    cHeight: 0,
                    scx:0,//對於單手操作是移動的起點座標,對於縮放是圖片距離兩手指的中點最近的圖示。
                    scy:0,
                    iX:0,
                    iY:0,
                }
            },
            mounted() {
                setTimeout(() => {
                    this.initClipper();
                }, 10);
            },
            beforeDestroy() {
                let getGesture = this.$refs.gesture;
                getGesture.ontouchstart = null;
                getGesture.ontouchmove = null;
                getGesture.outouchend = null;
            },
            methods: {
                initClipper() {
                    const that = this;
                    that.loadImgQueue.push(that.img);
                    that.initCanvas();
                    that.loadImg();
                    // that.initCanvas();
                },
                initCanvas() {
                    const that = this;
                    let getCanvas = that.$refs.canvas,
                        getPCanvas = that.$refs.pCanvas,
                        clipperClientRect = that.$refs.clipper.getBoundingClientRect(),
                        clipperWidth = parseInt(that.clipperImgWidth / window.devicePixelRatio),
                        clipperHeight = parseInt(that.clipperImgHeight / window.devicePixelRatio);
                    that.ctx = getCanvas.getContext('2d');
                    that.pCtx = getPCanvas.getContext('2d');
                    //判斷clipperWidth與clipperHeight有沒有超過容器值
                    if (clipperWidth < 0 || clipperWidth > clipperClientRect.width) {
                        clipperWidth = 250
                    }
                    if (clipperHeight < 0 || clipperHeight > clipperClientRect.height) {
                        clipperHeight = 100
                    }
                    //因為canvas在手機上會被放大,因此裡面的內容會模糊,這裡根據手機的devicePixelRatio來放大canvas,然後再通過設定css來收縮,因此關於canvas的所有值或座標都要乘以devicePixelRatio
                    getCanvas.style.width = clipperClientRect.width + 'px';
                    getCanvas.style.height = clipperClientRect.height-50 + 'px';
                    getCanvas.width = that.ratio(clipperClientRect.width);
                    getCanvas.height = that.ratio(clipperClientRect.height-50);
                    getPCanvas.style.width = clipperWidth + 'px';
                    getPCanvas.style.height = clipperHeight-50 + 'px';
                    getPCanvas.width = that.ratio(clipperWidth);
                    getPCanvas.height = that.ratio(clipperHeight-50);
                    //計算兩個canvas原點的x y差值
                    let cClientRect = getCanvas.getBoundingClientRect(),
                        pClientRect = getPCanvas.getBoundingClientRect();
                    that.originXDiff = pClientRect.left - cClientRect.left;
                    that.originYDiff = pClientRect.top - cClientRect.top;
                    that.cWidth = cClientRect.width;
                    that.cHeight = cClientRect.height;
                },
                getTouchstart(event){
                    const that = this;
                    let cClientRect = this.$refs.canvas.getBoundingClientRect(),
                        fingers = {}; //記錄當前有多少隻手指在觸控螢幕
                    event.preventDefault();
                    //two finger
                    let figureDistance = 0;
                    if (!that.imgLoaded) {
                        return;
                    }
                    if (event.touches.length === 1) {
                        let finger = event.touches[0];
    
                        that.scx = finger.pageX;
                        that.scy = finger.pageY;
                        that.iX = that.imgX;
                        that.iY = that.imgY;
                        fingers[finger.identifier] = finger;
                    } else if (event.touches.length === 2) {
                        let finger1 = event.touches[0],
                            finger2 = event.touches[1],
                            f1x = finger1.pageX - cClientRect.left,
                            f1y = finger1.pageY - cClientRect.top,
                            f2x = finger2.pageX - cClientRect.left,
                            f2y = finger2.pageY - cClientRect.top;
    
                        that.scx = parseInt((f1x + f2x) / 2);
                        that.scy = parseInt((f1y + f2y) / 2);
                        figureDistance = that.pointDistance(f1x, f1y, f2x, f2y);
                        fingers[finger1.identifier] = finger1;
                        fingers[finger2.identifier] = finger2;
    
                        //判斷變換中點是否在圖片中,如果不是則去離圖片最近的點
                        if (that.scx < that.imgX) {
                            that.scx = that.imgX;
                        }
                        if (that.scx > that.imgX + that.imgCurrentWidth) {
                            that.scx = that.imgX + that.imgCurrentHeight;
                        }
                        if (that.scy < that.imgY) {
                            that.scy = that.imgY;
                        }
                        if (that.scy > that.imgY + that.imgCurrentHeight) {
                            that.scy = that.imgY + that.imgCurrentHeight;
                        }
                    }
                },
                getTouchmove(event){
                    const that = this;
                    let cClientRect = that.$refs.canvas.getBoundingClientRect(),
                        fingers = {}; //記錄當前有多少隻手指在觸控螢幕
                    //two finger
                    let figureDistance = 0,
                        pinchScale = that.imgScale;
                    event.preventDefault();
                    if (!that.imgLoaded) {
                        return;
                    }
                    that.maskShowTimer && clearTimeout(that.maskShowTimer);
                    that.maskShow = false;
    
                    if (event.touches.length === 1) {
                        let f1x = event.touches[0].pageX,
                            f1y = event.touches[0].pageY;
                        that.drawImage(that.iX + f1x - that.scx, that.iY + f1y - that.scy, that.imgCurrentWidth, that.imgCurrentHeight);
                    } else if (event.touches.length === 2) {
                        let finger1 = event.touches[0],
                            finger2 = event.touches[1],
                            f1x = finger1.pageX - cClientRect.left,
                            f1y = finger1.pageY - cClientRect.top,
                            f2x = finger2.pageX - cClientRect.left,
                            f2y = finger2.pageY - cClientRect.top,
                            newFigureDistance = that.pointDistance(f1x, f1y, f2x, f2y),
                            scale = that.imgScale + parseFloat(((newFigureDistance - figureDistance) / that.imgScaleStep).toFixed(1));
    
                        fingers[finger1.identifier] = finger1;
                        fingers[finger2.identifier] = finger2;
    
                        if (scale !== pinchScale) {
                            //目前縮放的最小比例是1,最大是5
                            if (scale < that.imgMinScale) {
                                scale = that.imgMinScale;
                            } else if (scale > that.imgMaxScale) {
                                scale = that.imgMaxScale;
                            }
    
                            pinchScale = scale;
                            that.scale(that.scx, that.scy, scale);
                        }
                    }
                },
                getTouchend(event){
                    const that = this;
                    let fingers = {}; //記錄當前有多少隻手指在觸控螢幕
                    //two finger
                    let pinchScale = that.imgScale;
                    if (!that.imgLoaded) {
                        return;
                    }
                    that.imgScale = pinchScale;
    
                    //從finger刪除已經離開的手指
                    let touches = Array.prototype.slice.call(event.changedTouches, 0);
    
                    touches.forEach(item => {
                        delete fingers[item.identifier];
                    });
    
                    //迭代fingers,如果存在finger則更新scx,scy,iX,iY,因為可能縮放後立即單指拖動
                    let i,
                        fingerArr = [];
    
                    for(i in fingers) {
                        if (fingers.hasOwnProperty(i)) {
                            fingerArr.push(fingers[i]);
                        }
                    }
    
                    if (fingerArr.length > 0) {
                        that.scx = fingerArr[0].pageX;
                        that.scy = fingerArr[0].pageY;
                    } else {
                        that.maskShowTimer = setTimeout(() => {
                            that.maskShow = true;
                        }, 300);
                    }
    
                    //做邊界值檢測
                    let x = that.imgX,
                        y = that.imgY,
                        pClientRect = that.$refs.pCanvas.getBoundingClientRect();
    
                    if (x > pClientRect.left + pClientRect.width) {
                        x = pClientRect.left
                    } else if (x + that.imgCurrentWidth < pClientRect.left) {
                        x = pClientRect.left + pClientRect.width - that.imgCurrentWidth;
                    }
    
                    if (y > pClientRect.top + pClientRect.height) {
                        y = pClientRect.top;
                    } else if (y + that.imgCurrentHeight < pClientRect.top) {
                        y = pClientRect.top + pClientRect.height - that.imgCurrentHeight;
                    }
    
                    if (that.imgX !== x || that.imgY !== y) {
                        that.drawImage(x, y, that.imgCurrentWidth, that.imgCurrentHeight);
                    }
                },
                loadImg() {
                    const that = this;
                    if (that.imgLoading || that.loadImgQueue.length === 0) {
                        return;
                    }
                    let img = that.loadImgQueue.shift();
    
                    if (!img) {
                        return;
                    }
                    let newImage = new Image(),
                        onLoad = e => {
                            newImage.removeEventListener('load', onLoad, false);
                            that.imageData = newImage;
                            that.imgLoaded = true;
                            that.imgLoading = false;
                            that.initImg(newImage.width, newImage.height);
                            that.loadImg();
                        },
                        onError = e => {
                            newImage.removeEventListener('error', onError, false);
                            that.imageData = newImage = null;
                            that.imgLoading = false;
                            that.loadImg();
                        };
                    that.imgLoading = true;
                    that.imgLoaded = false;
                    newImage.src = that.img;
                    newImage.crossOrigin = 'Anonymous'; //因為canvas toDataUrl不能操作未經允許的跨域圖片,這需要伺服器設定Access-Control-Allow-Origin頭
                    newImage.addEventListener('load', onLoad, false);
                    newImage.addEventListener('error', onError, false);
                },
                initImg(w, h) {
                    const that = this;
                    let eW = null,
                        eH = null,
                        maxW = that.cWidth,
                        maxH = that.cHeight - that.actionBarHeight;
    
                    //如果圖片的寬高都少於容器的寬高,則不做處理
                    if (w <= maxW && h <= maxH) {
                        eW = w;
                        eH = h;
                    } else if (w > maxW && h <= maxH) {
                        eW = maxW;
                        eH = parseInt(h / w * maxW);
                    } else if (w <= maxW && h > maxH) {
                        eW = parseInt(w / h * maxH);
                        eH = maxH;
                    } else {
                        //判斷是橫圖還是豎圖
                        if (h > w) {
                            eW = parseInt(w / h * maxH);
                            eH = maxH;
                        } else {
                            eW = maxW;
                            eH = parseInt(h / w * maxW);
                        }
                    }
    
                    if (eW <= maxW && eH <= maxH) {
                        //記錄其初始化的寬高,日後的縮放功能以此值為基礎
                        that.imgStartWidth = eW;
                        that.imgStartHeight = eH;
                        that.drawImage((maxW - eW) / 2, (maxH - eH) / 2, eW, eH);
                    } else {
                        that.initImg(eW, eH);
                    }
                },
                drawImage(x, y, w, h) {
                    const that = this;
                    that.clearCanvas();
                    that.imgX = parseInt(x);
                    that.imgY = parseInt(y);
                    that.imgCurrentWidth = parseInt(w);
                    that.imgCurrentHeight = parseInt(h);
    
                    //更新canvas
                    that.ctx.drawImage(that.imageData, that.ratio(x), that.ratio(y), that.ratio(w), that.ratio(h));
    
                    //更新pCanvas,只需要減去兩個canvas座標原點對應的差值即可
                    // this.pCtx.drawImage(this.imageData, this.ratio(x - this.originXDiff), this.ratio(y - this.originYDiff), this.ratio(w), this.ratio(h));
                },
                clearCanvas() {
                    const that = this;
                    let getCanvas = that.$refs.canvas,
                        getPCanvas = that.$refs.pCanvas;
                    getCanvas.width = getCanvas.width;
                    getCanvas.height = getCanvas.height;
                    getPCanvas.width = getPCanvas.width;
                    getPCanvas.height = getPCanvas.height;
                },
                ratio(size) {
                    return parseInt(window.devicePixelRatio * size);
                },
                pointDistance(x1, y1, x2, y2) {
                    return parseInt(Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)));
                },
                scale(x, y, scale) {
                    const that = this;
                    let newPicWidth = parseInt(that.imgStartWidth * scale),
                        newPicHeight = parseInt(that.imgStartHeight * scale),
                        newIX = parseInt(x - newPicWidth * (x - that.imgX) / that.imgCurrentWidth),
                        newIY = parseInt(y - newPicHeight * (y - that.imgY) / that.imgCurrentHeight);
                    that.drawImage(newIX, newIY, newPicWidth, newPicHeight);
                },
                clipper() {
                    const that = this;
                    let imgData = null;
                    try {
                        imgData = that.$refs.pCanvas.toDataURL();
                    } catch (e) {
                        console.error('請在response header加上Access-Control-Allow-Origin,否則canvas無法裁剪未經許可的跨域圖片');
                    }
                    this.$emit('getNewImage', imgData);
                },
                cancel() {
                    this.$emit('cancel');
                },
                getBase64(dataURL) {
                    return dataURL.replace(/^data:image\/(png|jpg);base64,/, '');
                }
            }
        }
    </script>

     

最後,ε=(´ο`*)))唉路漫漫其修遠兮,我這隻後端的小菜鳥在前端的路上還要奮力向前,不說了接著接收運營大佬的神仙建議去了~~

相關文章