使用Hammer.js实现捏合缩放

42

我正在尝试使用 hammer.js 实现捏合缩放。

这是我的HTML代码 -

 <script src="//cdnjs.cloudflare.com/ajax/libs/hammer.js/1.0.5/hammer.min.js"></script>

<div id="pinchzoom">
        <div>
            <img id="rect" src="http://blog.gettyimages.com/wp-content/uploads/2013/01/Siberian-Tiger-Running-Through-Snow-Tom-Brakefield-Getty-Images-200353826-001.jpg" width="2835" height="4289" ondragstart="return false" alt="" />
        </div>
    </div>

这是我的SCRIPT

var hammertime = Hammer(document.getElementById('pinchzoom'), {
        transform_always_block: true,
        transform_min_scale: 1,
        drag_block_horizontal: false,
        drag_block_vertical: false,
        drag_min_distance: 0
    });

    var rect = document.getElementById('rect');

    var posX=0, posY=0,
        scale=1, last_scale,
        rotation= 1, last_rotation;

    hammertime.on('touch drag transform', function(ev) {
        switch(ev.type) {
            case 'touch':
                last_scale = scale;
                last_rotation = rotation;
                break;

            case 'drag':
                posX = ev.gesture.deltaX;
                posY = ev.gesture.deltaY;
                break;

            case 'transform':
                rotation = last_rotation + ev.gesture.rotation;
                scale = Math.max(1, Math.min(last_scale * ev.gesture.scale, 10));
                break;
        }

        // transform!
        var transform =
                //"translate3d("+posX+"px,"+posY+"px, 0) " +
                "scale3d("+scale+","+scale+", 0) ";


        rect.style.transform = transform;
        rect.style.oTransform = transform;
        rect.style.msTransform = transform;
        rect.style.mozTransform = transform;
        rect.style.webkitTransform = transform;
    });

它功能正常,但我无法滚动图像。取消注释transform3d后,它可以工作,但拖动时图像会失去位置。我不能使用jQuery。

5个回答

67

对于 2.0+,我采用了 @DGS 的答案并进行了修改以适应我在 上的操作,因此它是纯JS和 2.0以适应

我的实现允许您同时缩放和拖动,不像上面那样彼此独立,并提供更本地的感觉。它还实现了双击缩放(和缩小回去)。我将其设置为在.9994之间缩放,但您可以根据需要更改这些值。因此,如果只需复制并粘贴此代码,则可能会按预期工作()。

感谢Eight Media和@DGS的启发!请随意在SO中改进它。

function hammerIt(elm) {
    hammertime = new Hammer(elm, {});
    hammertime.get('pinch').set({
        enable: true
    });
    var posX = 0,
        posY = 0,
        scale = 1,
        last_scale = 1,
        last_posX = 0,
        last_posY = 0,
        max_pos_x = 0,
        max_pos_y = 0,
        transform = "",
        el = elm;

    hammertime.on('doubletap pan pinch panend pinchend', function(ev) {
        if (ev.type == "doubletap") {
            transform =
                "translate3d(0, 0, 0) " +
                "scale3d(2, 2, 1) ";
            scale = 2;
            last_scale = 2;
            try {
                if (window.getComputedStyle(el, null).getPropertyValue('-webkit-transform').toString() != "matrix(1, 0, 0, 1, 0, 0)") {
                    transform =
                        "translate3d(0, 0, 0) " +
                        "scale3d(1, 1, 1) ";
                    scale = 1;
                    last_scale = 1;
                }
            } catch (err) {}
            el.style.webkitTransform = transform;
            transform = "";
        }

        //pan    
        if (scale != 1) {
            posX = last_posX + ev.deltaX;
            posY = last_posY + ev.deltaY;
            max_pos_x = Math.ceil((scale - 1) * el.clientWidth / 2);
            max_pos_y = Math.ceil((scale - 1) * el.clientHeight / 2);
            if (posX > max_pos_x) {
                posX = max_pos_x;
            }
            if (posX < -max_pos_x) {
                posX = -max_pos_x;
            }
            if (posY > max_pos_y) {
                posY = max_pos_y;
            }
            if (posY < -max_pos_y) {
                posY = -max_pos_y;
            }
        }


        //pinch
        if (ev.type == "pinch") {
            scale = Math.max(.999, Math.min(last_scale * (ev.scale), 4));
        }
        if(ev.type == "pinchend"){last_scale = scale;}

        //panend
        if(ev.type == "panend"){
            last_posX = posX < max_pos_x ? posX : max_pos_x;
            last_posY = posY < max_pos_y ? posY : max_pos_y;
        }

        if (scale != 1) {
            transform =
                "translate3d(" + posX + "px," + posY + "px, 0) " +
                "scale3d(" + scale + ", " + scale + ", 1)";
        }

        if (transform) {
            el.style.webkitTransform = transform;
        }
    });
}

在元素加载后,只需使用 hammerIt(document.getElementById("imagid")) 来实现。你可以对任意多个元素调用此函数。

干杯!


这很棒,但是我对“scale!= 1”的测试有点困惑。除非我开始捏,否则我的比例始终为1,这意味着那些手势被跳过了。 - Jason Farnsworth
1
@JasonFarnsworth 如果图像没有放大,就无法平移/拖动它,因此除非缩放比例>1,否则运行该代码没有意义。 - K'shin Gendron
啊,明白了。我正在使用不同的方法--让用户平移,如果他们越界,然后我会过渡回到一个合理的状态。 - Jason Farnsworth
你为什么使用0.999而不是1作为最大值?使用0.999后,经过一次放大和完全缩小,比例尺达到了0.999,但原始值应该是1。 - glerendegui
这个很好用,唯一的问题是忽略了swipeup和swipedown事件,因此当触摸在元素上时用户无法滚动页面。 - Mike Purcell
显示剩余2条评论

15

添加变量last_posXlast_posY以考虑平移时位置的变化。在拖动结束时更新它们,这样下次捕获拖动事件时就会考虑上一次拖动结束的位置。

var posX=0, posY=0,
    scale=1, last_scale,
    last_posX=0, last_posY=0,
    max_pos_x=0, max_pos_y=0;

hammertime.on('touch drag transform dragend', function(ev) {
    switch(ev.type) {
        case 'touch':
            last_scale = scale;
            break;

        case 'drag':
            if(scale != 1){
                    posX = last_posX + ev.gesture.deltaX;
                    posY = last_posY + ev.gesture.deltaY;
                    if(posX > max_pos_x){
                        posX = max_pos_x;
                    }
                    if(posX < -max_pos_x){
                        posX = -max_pos_x;
                    }
                    if(posY > max_pos_y){
                        posY = max_pos_y;
                    }
                    if(posY < -max_pos_y){
                        posY = -max_pos_y;
                    }
            }else{
                posX = 0;
                posY = 0;
                saved_posX = 0;
                saved_posY = 0;
            }
            break;

        case 'transform':
            scale = Math.max(1, Math.min(last_scale * ev.gesture.scale, 10));
            max_pos_x = Math.ceil((scale - 1) * rect.clientWidth / 2);
            max_pos_y = Math.ceil((scale - 1) * rect.clientHeight / 2);
            if(posX > max_pos_x){
                posX = max_pos_x;
            }
            if(posX < -max_pos_x){
                posX = -max_pos_x;
            }
            if(posY > max_pos_y){
                posY = max_pos_y;
            }
            if(posY < -max_pos_y){
                posY = -max_pos_y;
            }
            break;
        case 'dragend':
            last_posX = posX < max_pos_x ? posX: max_pos_x;
            last_posY = posY < max_pos_y ? posY: max_pos_y;
            break;
    }

    // transform!
    var transform =
            "translate3d(0, 0, 0) " +
            "scale3d(1, 1, 0) "; 
    if(scale != 1){
        transform =
            "translate3d("+posX+"px,"+posY+"px, 0) " +
            "scale3d("+scale+","+scale+", 0) ";
    }

    rect.style.transform = transform;
    rect.style.oTransform = transform;
    rect.style.msTransform = transform;
    rect.style.mozTransform = transform;
    rect.style.webkitTransform = transform;
});

@DGS- 我想保留原始图像大小,即每当比例为1或更小时,图像应在收缩时返回到其原始大小和位置。 - nickalchemist
1
@Nickalchemist 更新了答案。现在,如果比例为1或更小,则CSS翻译和比例将重置为默认值。 - DGS
@DGS- 位置仍未固定。我仍然能够在0比例下拖动图像。 - nickalchemist
你的意思是想要限制滚动范围,不让用户超出图片边界滚动? - DGS
更新的代码已经测试过了,看起来能够完成你想要它做的一切。 - DGS
显示剩余10条评论

10

我对这些解决方案并不满意,所以我也创建了一个解决方案。

您可以在https://bl.ocks.org/stephanbogner/06c3e0d3a1c8fcca61b5345e1af80798上进行测试。

代码更复杂,但与iOS上的行为相同(在iPad和iPhone上进行了测试):

  • 您可以双击图像,它会缩放到精确位置
  • 在捏合缩放时,缩放中心位于手指之间,您可以在捏合时拖动图像

<!DOCTYPE html>
<html> 
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>HammerJS Pinch Zoom and Drag</title>
    <style type="text/css">
    body{
        margin: 0;
        padding: 0;
    }
    #dragWrapper{
        margin: 40px;
        background: whitesmoke;
        width: calc(100vw - 80px);
        height: calc(100vh - 80px);
        position: relative;
    }
    #drag {
        touch-action: auto;
        height: 300px;
        width: 200px;
        position: absolute;
        left: 0;
        top: 0;
        background-image: url('https://upload.wikimedia.org/wikipedia/commons/thumb/4/4a/Bergelut_dengan_asap_nan_beracun.jpg/318px-Bergelut_dengan_asap_nan_beracun.jpg');
            background-size: cover;
    }
    </style>
</head>
<body>
    <div id="dragWrapper">
        <div id="drag"></div>
    </div>
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/hammer.js/2.0.8/hammer.min.js"></script>
    <script type="text/javascript">
    var element = document.getElementById('drag')
    var hammertime = new Hammer(element, {});
    hammertime.get('pinch').set({ enable: true });
    hammertime.get('pan').set({ threshold: 0 });
    var fixHammerjsDeltaIssue = undefined;
    var pinchStart = { x: undefined, y: undefined }
    var lastEvent = undefined;
    var originalSize = {
        width: 200,
        height: 300
    }
    var current = {
        x: 0,
        y: 0,
        z: 1,
        zooming: false,
        width: originalSize.width * 1,
        height: originalSize.height * 1,
    }
    var last = {
        x: current.x,
        y: current.y,
        z: current.z
    }
    function getRelativePosition(element, point, originalSize, scale) {
        var domCoords = getCoords(element);
        var elementX = point.x - domCoords.x;
        var elementY = point.y - domCoords.y;
        var relativeX = elementX / (originalSize.width * scale / 2) - 1;
        var relativeY = elementY / (originalSize.height * scale / 2) - 1;
        return { x: relativeX, y: relativeY }
    }
    function getCoords(elem) { // crossbrowser version
        var box = elem.getBoundingClientRect();
        var body = document.body;
        var docEl = document.documentElement;
        var scrollTop = window.pageYOffset || docEl.scrollTop || body.scrollTop;
        var scrollLeft = window.pageXOffset || docEl.scrollLeft || body.scrollLeft;
        var clientTop = docEl.clientTop || body.clientTop || 0;
        var clientLeft = docEl.clientLeft || body.clientLeft || 0;
        var top  = box.top +  scrollTop - clientTop;
        var left = box.left + scrollLeft - clientLeft;
        return { x: Math.round(left), y: Math.round(top) };
    }
    function scaleFrom(zoomOrigin, currentScale, newScale) {
        var currentShift = getCoordinateShiftDueToScale(originalSize, currentScale);
        var newShift = getCoordinateShiftDueToScale(originalSize, newScale)
        var zoomDistance = newScale - currentScale

        var shift = {
            x: currentShift.x - newShift.x,
            y: currentShift.y - newShift.y,
        }
        var output = {
            x: zoomOrigin.x * shift.x,
            y: zoomOrigin.y * shift.y,
            z: zoomDistance
        }
        return output
    }
    function getCoordinateShiftDueToScale(size, scale){
        var newWidth = scale * size.width;
        var newHeight = scale * size.height;
        var dx = (newWidth - size.width) / 2
        var dy = (newHeight - size.height) / 2
        return {
            x: dx,
            y: dy
        }
    }
    hammertime.on('doubletap', function(e) {
        var scaleFactor = 1;
        if (current.zooming === false) {
            current.zooming = true;
        } else {
            current.zooming = false;
            scaleFactor = -scaleFactor;
        }
        element.style.transition = "0.3s";
        setTimeout(function() {
            element.style.transition = "none";
        }, 300)
        var zoomOrigin = getRelativePosition(element, { x: e.center.x, y: e.center.y }, originalSize, current.z);
        var d = scaleFrom(zoomOrigin, current.z, current.z + scaleFactor)
        current.x += d.x;
        current.y += d.y;
        current.z += d.z;
        last.x = current.x;
        last.y = current.y;
        last.z = current.z;
        update();
    })
    hammertime.on('pan', function(e) {
        if (lastEvent !== 'pan') {
            fixHammerjsDeltaIssue = {
                x: e.deltaX,
                y: e.deltaY
            }
        }
        current.x = last.x + e.deltaX - fixHammerjsDeltaIssue.x;
        current.y = last.y + e.deltaY - fixHammerjsDeltaIssue.y;
        lastEvent = 'pan';
        update();
    })    
    hammertime.on('pinch', function(e) {
        var d = scaleFrom(pinchZoomOrigin, last.z, last.z * e.scale)
        current.x = d.x + last.x + e.deltaX;
        current.y = d.y + last.y + e.deltaY;
        current.z = d.z + last.z;
        lastEvent = 'pinch';
        update();
    })
    var pinchZoomOrigin = undefined;
    hammertime.on('pinchstart', function(e) {
        pinchStart.x = e.center.x;
        pinchStart.y = e.center.y;
        pinchZoomOrigin = getRelativePosition(element, { x: pinchStart.x, y: pinchStart.y }, originalSize, current.z);
        lastEvent = 'pinchstart';
    })
    hammertime.on('panend', function(e) {
        last.x = current.x;
        last.y = current.y;
        lastEvent = 'panend';
    })
    hammertime.on('pinchend', function(e) {
        last.x = current.x;
        last.y = current.y;
        last.z = current.z;
        lastEvent = 'pinchend';
    })
    function update() {
        current.height = originalSize.height * current.z;
        current.width = originalSize.width * current.z;
        element.style.transform = "translate3d(" + current.x + "px, " + current.y + "px, 0) scale(" + current.z + ")";
    }
    </script>
</body>

</html>

我非常想尝试它,但链接无法访问。 - goldenriver4422
1
我认为bl.ocks.org很快就会上线,但是你可以随意将我的代码复制到“index.html”文件中并将其托管在服务器上。或者您也可以启动本地主机,并在移动设备上查看它 - st_phan

3

请查看使用HammerJS实现的Pinch缩放和平移演示。该示例已在Android、iOS和Windows Phone上进行了测试。

您可以在Pinch Zoom and Pan with HammerJS找到源代码。

为了方便起见,这里是源代码:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport"
        content="user-scalable=no, width=device-width, initial-scale=1, maximum-scale=1">
  <title>Pinch Zoom</title>
</head>

<body>

  <div>

    <div style="height:150px;background-color:#eeeeee">
      Ignore this area. Space is needed to test on the iPhone simulator as pinch simulation on the
      iPhone simulator requires the target to be near the middle of the screen and we only respect
      touch events in the image area. This space is not needed in production.
    </div>

    <style>

      .pinch-zoom-container {
        overflow: hidden;
        height: 300px;
      }

      .pinch-zoom-image {
        width: 100%;
      }

    </style>

    <script src="https://hammerjs.github.io/dist/hammer.js"></script>

    <script>

      var MIN_SCALE = 1; // 1=scaling when first loaded
      var MAX_SCALE = 64;

      // HammerJS fires "pinch" and "pan" events that are cumulative in nature and not
      // deltas. Therefore, we need to store the "last" values of scale, x and y so that we can
      // adjust the UI accordingly. It isn't until the "pinchend" and "panend" events are received
      // that we can set the "last" values.

      // Our "raw" coordinates are not scaled. This allows us to only have to modify our stored
      // coordinates when the UI is updated. It also simplifies our calculations as these
      // coordinates are without respect to the current scale.

      var imgWidth = null;
      var imgHeight = null;
      var viewportWidth = null;
      var viewportHeight = null;
      var scale = null;
      var lastScale = null;
      var container = null;
      var img = null;
      var x = 0;
      var lastX = 0;
      var y = 0;
      var lastY = 0;
      var pinchCenter = null;

      // We need to disable the following event handlers so that the browser doesn't try to
      // automatically handle our image drag gestures.
      var disableImgEventHandlers = function () {
        var events = ['onclick', 'onmousedown', 'onmousemove', 'onmouseout', 'onmouseover',
                      'onmouseup', 'ondblclick', 'onfocus', 'onblur'];

        events.forEach(function (event) {
          img[event] = function () {
            return false;
          };
        });
      };

      // Traverse the DOM to calculate the absolute position of an element
      var absolutePosition = function (el) {
        var x = 0,
          y = 0;

        while (el !== null) {
          x += el.offsetLeft;
          y += el.offsetTop;
          el = el.offsetParent;
        }

        return { x: x, y: y };
      };

      var restrictScale = function (scale) {
        if (scale < MIN_SCALE) {
          scale = MIN_SCALE;
        } else if (scale > MAX_SCALE) {
          scale = MAX_SCALE;
        }
        return scale;
      };

      var restrictRawPos = function (pos, viewportDim, imgDim) {
        if (pos < viewportDim/scale - imgDim) { // too far left/up?
          pos = viewportDim/scale - imgDim;
        } else if (pos > 0) { // too far right/down?
          pos = 0;
        }
        return pos;
      };

      var updateLastPos = function (deltaX, deltaY) {
        lastX = x;
        lastY = y;
      };

      var translate = function (deltaX, deltaY) {
        // We restrict to the min of the viewport width/height or current width/height as the
        // current width/height may be smaller than the viewport width/height

        var newX = restrictRawPos(lastX + deltaX/scale,
                                  Math.min(viewportWidth, curWidth), imgWidth);
        x = newX;
        img.style.marginLeft = Math.ceil(newX*scale) + 'px';

        var newY = restrictRawPos(lastY + deltaY/scale,
                                  Math.min(viewportHeight, curHeight), imgHeight);
        y = newY;
        img.style.marginTop = Math.ceil(newY*scale) + 'px';
      };

      var zoom = function (scaleBy) {
        scale = restrictScale(lastScale*scaleBy);

        curWidth = imgWidth*scale;
        curHeight = imgHeight*scale;

        img.style.width = Math.ceil(curWidth) + 'px';
        img.style.height = Math.ceil(curHeight) + 'px';

        // Adjust margins to make sure that we aren't out of bounds
        translate(0, 0);
      };

      var rawCenter = function (e) {
        var pos = absolutePosition(container);

        // We need to account for the scroll position
        var scrollLeft = window.pageXOffset ? window.pageXOffset : document.body.scrollLeft;
        var scrollTop = window.pageYOffset ? window.pageYOffset : document.body.scrollTop;

        var zoomX = -x + (e.center.x - pos.x + scrollLeft)/scale;
        var zoomY = -y + (e.center.y - pos.y + scrollTop)/scale;

        return { x: zoomX, y: zoomY };
      };

      var updateLastScale = function () {
        lastScale = scale;
      };

      var zoomAround = function (scaleBy, rawZoomX, rawZoomY, doNotUpdateLast) {
        // Zoom
        zoom(scaleBy);

        // New raw center of viewport
        var rawCenterX = -x + Math.min(viewportWidth, curWidth)/2/scale;
        var rawCenterY = -y + Math.min(viewportHeight, curHeight)/2/scale;

        // Delta
        var deltaX = (rawCenterX - rawZoomX)*scale;
        var deltaY = (rawCenterY - rawZoomY)*scale;

        // Translate back to zoom center
        translate(deltaX, deltaY);

        if (!doNotUpdateLast) {
          updateLastScale();
          updateLastPos();
        }
      };

      var zoomCenter = function (scaleBy) {
        // Center of viewport
        var zoomX = -x + Math.min(viewportWidth, curWidth)/2/scale;
        var zoomY = -y + Math.min(viewportHeight, curHeight)/2/scale;

        zoomAround(scaleBy, zoomX, zoomY);
      };

      var zoomIn = function () {
        zoomCenter(2);
      };

      var zoomOut = function () {
        zoomCenter(1/2);
      };

      var onLoad = function () {

        img = document.getElementById('pinch-zoom-image-id');
        container = img.parentElement;

        disableImgEventHandlers();

        imgWidth = img.width;
        imgHeight = img.height;
        viewportWidth = img.offsetWidth;
        scale = viewportWidth/imgWidth;
        lastScale = scale;
        viewportHeight = img.parentElement.offsetHeight;
        curWidth = imgWidth*scale;
        curHeight = imgHeight*scale;

        var hammer = new Hammer(container, {
          domEvents: true
        });

        hammer.get('pinch').set({
          enable: true
        });

        hammer.on('pan', function (e) {
          translate(e.deltaX, e.deltaY);
        });

        hammer.on('panend', function (e) {
          updateLastPos();
        });

        hammer.on('pinch', function (e) {

          // We only calculate the pinch center on the first pinch event as we want the center to
          // stay consistent during the entire pinch
          if (pinchCenter === null) {
            pinchCenter = rawCenter(e);
            var offsetX = pinchCenter.x*scale - (-x*scale + Math.min(viewportWidth, curWidth)/2);
            var offsetY = pinchCenter.y*scale - (-y*scale + Math.min(viewportHeight, curHeight)/2);
            pinchCenterOffset = { x: offsetX, y: offsetY };
          }

          // When the user pinch zooms, she/he expects the pinch center to remain in the same
          // relative location of the screen. To achieve this, the raw zoom center is calculated by
          // first storing the pinch center and the scaled offset to the current center of the
          // image. The new scale is then used to calculate the zoom center. This has the effect of
          // actually translating the zoom center on each pinch zoom event.
          var newScale = restrictScale(scale*e.scale);
          var zoomX = pinchCenter.x*newScale - pinchCenterOffset.x;
          var zoomY = pinchCenter.y*newScale - pinchCenterOffset.y;
          var zoomCenter = { x: zoomX/newScale, y: zoomY/newScale };

          zoomAround(e.scale, zoomCenter.x, zoomCenter.y, true);
        });

        hammer.on('pinchend', function (e) {
          updateLastScale();
          updateLastPos();
          pinchCenter = null;
        });

        hammer.on('doubletap', function (e) {
          var c = rawCenter(e);
          zoomAround(2, c.x, c.y);
        });

      };

    </script>

    <button onclick="zoomIn()">Zoom In</button>
    <button onclick="zoomOut()">Zoom Out</button>

    <div class="pinch-zoom-container">
      <img id="pinch-zoom-image-id" class="pinch-zoom-image" onload="onLoad()"
           src="https://hammerjs.github.io/assets/img/pano-1.jpg">
    </div>


  </div>

</body>
</html>

顺便提一句,上面有很多好的信息,但我无法使任何示例实际工作,并且有一些细微的细节我想要实现,例如考虑缩放中心。


在onLoad()函数中有一个小错误。“viewportWidth = img.offsetWidth;”应该是“viewportWidth = img.parentElement offsetWidth;”。 - lhrec_106

1

这里有一个使用paper.js(codepen)相对紧凑的解决方案:

function initPinchZoom() {
    const canvasElement = paper.view.element;
    const box = canvasElement.getBoundingClientRect();
    const offset = new paper.Point(box.left, box.top);

    const hammer = new Hammer(canvasElement, {});
    hammer.get('pinch').set({ enable: true });

    let startMatrix, startMatrixInverted, p0ProjectCoords;

    hammer.on('pinchstart', e => {
        startMatrix = paper.view.matrix.clone();
        startMatrixInverted = startMatrix.inverted();
        const p0 = getCenterPoint(e);
        p0ProjectCoords = paper.view.viewToProject(p0);
    });

    hammer.on('pinch', e => {
        // Translate and scale view using pinch event's 'center' and 'scale' properties.
        // Translation computes center's distance from initial center (considering current scale).
        const p = getCenterPoint(e);
        const pProject0 = p.transform(startMatrixInverted);
        const delta = pProject0.subtract(p0ProjectCoords).divide(e.scale);
        paper.view.matrix = startMatrix.clone().scale(e.scale, p0ProjectCoords).translate(delta);
    });

    function getCenterPoint(e) {
        return new paper.Point(e.center.x, e.center.y).subtract(offset);
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接