最近需要做一个投票活动,上传图片时需要拖拽、缩放来裁剪图片,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>

最后,ε=(´ο`*)))唉路漫漫其修远兮,我这只后端的小菜鸟在前端的路上还要奋力向前,不说了接着接收运营大佬的神仙建议去了~~

拖拽一个元素如此简单,mouse、drag、touch三兄弟的用处的更多相关文章

  1. 从下拉菜单拖拽一个元素 出来,插入到页面中的app 列表中

    1,实现功能:从下拉菜单拖拽一个元素 出来,插入到页面中的app 列表中 并实现app向后移动一个元素的位置: 2.实现思路: 01.遍历下拉菜单,添加拖拽方法,实现位置移动功能: 02.遍历app列 ...

  2. [转]人人网首页拖拽上传详解(HTML5 Drag&Drop、FileReader API、formdata)

    人人网首页拖拽上传详解(HTML5 Drag&Drop.FileReader API.formdata) 2011年12月11日 | 彬Go 上一篇:给力的 Google HTML5 训练营( ...

  3. JQuery拖拽改变元素的尺寸

    "元素拖拽改变大小"其实和"元素拖拽"一个原理,只是所动态改变的对象不同而已,主要在于 top.left.width.height 的运用,相对实现起来也非常容 ...

  4. JavaScript动画-拖拽改变元素大小

    ▓▓▓▓▓▓ 大致介绍 拖拽改变元素大小是在模拟拖拽上增加了一些功能 效果:拖拽改变元素大小 ▓▓▓▓▓▓ 拖拽改变元素大小原理 首先这个方块得知道我们想要改变这个它的大小,所以我给它设定一个范围,当 ...

  5. jquery插件之拖拽改变元素大小

    该插件乃本博客作者所写,目的在于提升作者的js能力,也给一些js菜鸟在使用插件时提供一些便利,老鸟就悠然地飞过吧. 此插件旨在实现目前较为流行的拖拽改变元素大小的效果,您可以根据自己的实际需求来设置被 ...

  6. selenium学习-拖拽页面元素

    一.ActionChains包 模拟鼠标的操作要首先引入ActionChains的包 from selenium.webdriver.common.action_chains import Actio ...

  7. javascript动画系列第四篇——拖拽改变元素大小

    × 目录 [1]原理简介 [2]范围圈定 [3]大小改变[4]代码优化 前面的话 拖拽可以让元素移动,也可以改变元素大小.本文将详细介绍拖拽改变元素大小的效果实现 原理简介 拖拽让元素移动,是改变定位 ...

  8. iOS开发之--storyboary下,拖拽一个tableview/collectionView/view 等,顶端下沉64个像素的处理方法

    大家可能会发现,在sb或者xib里面拖拽一个tableview/collectionview/view的,顶端会自动下沉64个像素,也就是说,运行在模拟器上去,自导航下面又自动下沉了64个像素, 那是 ...

  9. 通过Ztree生成页面html元素Dom树,以及拖拽改变元素的位置

    zTree 是一款依靠 jQuery 实现的多功能 "树插件",http://www.treejs.cn/v3/main.php#_zTreeInfo,功能强大,不多赘述. 下面我 ...

随机推荐

  1. How tomcat works(深入剖析tomcat)生命周期Lifecycle

    How Tomcat Works (6)生命周期Lifecycle 总体概述 这一章讲的是tomcat的组件之一,LifeCycle组件,通过这个组件可以统一管理其他组件,可以达到统一启动/关闭组件的 ...

  2. PHP后台基本语法使用笔记

    1. PHP中报500错误时如何查看错误信息 //将如下的代码放入PHP的文件中ini_set("display_errors","On"); error_re ...

  3. PyQt(Python+Qt)学习随笔:Qt Designer中toolBar的toolButtonStyle属性

    tooButtonStyle属性保存主工具栏按钮的样式设置,用来表示工具栏按钮的文字和图标怎么显示. 该属性的可设置值类型为枚举类型Qt.ToolButtonStyle,它包含如下值: 该属性的缺省值 ...

  4. 0、tensorflow学习开始

    结合tensorflowtf 2.x , tensorflow 1.x, pytorch来深入理解深度学习架构,用博客来记录这一系列,日后也方便回顾,博客中也会加入个人理解和感悟 参考的博客列表如下: ...

  5. uniapp 微信授权登陆

    准备工作: 1.微信开发者账号 2.AppId .AppSecret (这些可以在开放平台申请到) 第一步 添加移动应用,仔细添加上述信息: 审批通过后,即刻 第二步 打开uniapp,开启OAuth ...

  6. Libp2p 简介

    这是一个翻译的系列文章,原文参考:Introduction :: libp2p Documentation 欢迎来阅读libp2p相关文档,不论你是刚开始学习如何用libp2p来搭建P2P系统, 还是 ...

  7. LeetCode初级算法之字符串:344 反转字符串

    反转字符串 题目地址:https://leetcode-cn.com/problems/reverse-string/ 编写一个函数,其作用是将输入的字符串反转过来.输入字符串以字符数组 char[] ...

  8. 题解-[SDOI2014]数数

    [SDOI2014]数数 这题的前置知识是AC自动机和dp,前置题目是 [JSOI2007]文本生成器,前置题目我写的题解 题解-[JSOI2007]文本生成器.我的讲解假设你做过上面那道题. 这题比 ...

  9. 题解-Magic Ship

    Magic Ship 你在 \((x_1,y_1)\),要到点 \((x_2,y_2)\).风向周期为 \(n\),一个字符串 \(s\{n\}\) 表示风向(每轮上下左右),每轮你都会被风向吹走一格 ...

  10. uniapp云打包配置讲解

    HBuilderX开发工具,菜单栏:发行(U) → 原生App云打包(P) 安卓云打包配置: 云打包配置分为公共测试证书和自有证书. 云打包配置使用公共测试证书很简单,直接勾选后打包. 如果要测试第三 ...