拉斐尔纸张缩放动画

10

我设法通过一些技巧对raphael画布进行缩放,因为设置视图框(setviewbox)对我来说不起作用,这是我编写的函数:

function setCTM(element, matrix) {
    var s = "matrix(" + matrix.a + "," + matrix.b + "," + matrix.c + "," + matrix.d + "," + matrix.e + "," + matrix.f + ")";

    element.setAttribute("transform", s);
}

Raphael.fn.zoomAndMove = function(coordx,coordy,zoom) {

    var svg = document.getElementsByTagName("svg")[0];

    var z = zoom;

    var g = document.getElementById("viewport1");
    var p = svg.createSVGPoint();

    p.x = coordx; 
    p.y = coordy;

    p = p.matrixTransform(g.getCTM().inverse());

    var k = svg.createSVGMatrix().scale(z).translate(-p.x, -p.y);
    setCTM(g, g.getCTM().multiply(k));
} 

其中视口元素1的定义为:

var gelem = document.createElementNS('http://www.w3.org/2000/svg', 'g');
gelem.id = 'viewport1';

paper.canvas.appendChild(gelem);
paper.canvas = gelem;

那么我可以调用:paper.zoomAndMove(minx,miny,zoomRatio);

有没有可能改变这个函数使其平滑缩放?


请提供一个基本示例,以查看当前的缩放效果。 - zbug
4个回答

14

对于像我这样希望可以实现平滑动画缩放和平移的人,可以看一下这里:

https://groups.google.com/forum/?fromgroups#!topic/raphaeljs/7eA9xq4enDo

这段代码帮助我自动化并动画化了在画布上缩放和平移到特定点的操作。(感谢Will Morgan)

Raphael.fn.animateViewBox = function(currentViewBox, viewX, viewY, width, height, duration, callback) {

    duration = duration || 250;

    var originals = currentViewBox, //current viewBox Data from where the animation should start
        differences = {
                x: viewX - originals.x,
                y: viewY - originals.y,
                width: width - originals.width,
                height: height - originals.height
        },
        delay = 13,
        stepsNum = Math.ceil(duration / delay),
        stepped = {
                x: differences.x / stepsNum,
                y: differences.y / stepsNum,
                width: differences.width / stepsNum,
                height: differences.height / stepsNum
        }, i,
        canvas = this;

    /**
     * Using a lambda to protect a variable with its own scope.
     * Otherwise, the variable would be incremented inside the loop, but its
     * final value would be read at run time in the future.
     */
    function timerFn(iterator) {
            return function() {
                    canvas.setViewBox(
                            originals.x + (stepped.x * iterator),
                            originals.y + (stepped.y * iterator),
                            originals.width + (stepped.width * iterator),
                            originals.height + (stepped.height * iterator)
                    );
                    // Run the callback as soon as possible, in sync with the last step
                    if(iterator == stepsNum && callback) {
                            callback(viewX, viewY, width, height);
                    }
            }
    }

    // Schedule each animation step in to the future
    // Todo: use some nice easing
    for(i = 1; i <= stepsNum; ++i) {
            setTimeout(timerFn(i), i * delay);
    }

} 

这正是我所需要的,我的唯一问题是如何生成“currentViewBox”参数?谢谢。 - Chris
4
希望这有帮助?var currentViewBox = { x: me.currPos.x, y: me.currPos.y, width: paper.width * (1 - (me.currZoom * settings.zoomStep)), height: paper.height * (1 - (me.currZoom * settings.zoomStep)) }; (注:该段内容为计算机代码,翻译时不应对其进行更改。) - patrics
这个能用于缩放折线图吗?从http://g.raphaeljs.com/?提供一个示例或Jsfiddle会很有帮助。 - django

11
请参考这个JSFiddle示例,它利用jQuery和Raphael的setViewBox功能实现了平滑的缩放和平移。我们在更复杂和更大的环境中使用完全相同的功能,即使屏幕上绘制了大量项目,它仍然可以完美地工作并保持流畅。

基本上,不要试图重新发明轮子。我知道这可能不是您想要的答案,但这几乎肯定是您最好的选择。

编辑:我已修复附带的fiddle(由于JSFiddle对网站进行了更改而导致故障),但显然SO不允许我保存没有包含一些代码的JSFiddle链接,因此...

console.log("hello world!");

1
您在fiddle中未将raphael添加为依赖项。只是提到它了 :)。这里是更新链接 - Mithun Satheesh
1
感谢@mithunsatheesh!我修复了我的fiddle并更新了链接。显然JSFiddle进行了一些更改,破坏了原始的fiddle。现在它又可以工作了。 - James

2
与上文类似,但有所不同:
  • 不依赖内部(已经改变)
  • 不需要任何全局设置(将为您处理)
  • 使用requestAnimationFrame,导致动画更加平滑(但不能在IE9或以下版本中运行
  • 使用Raphael的缓动函数lineareaseIneaseOuteaseInOutbackInbackOutelasticbounce

由于这是完全重写,我没有修改上述内容。

 Raphael.fn.animateViewBox = function(viewX, viewY, width, height, duration, callback, easing) {
    var start = window.performance.now(),
      canvas = this;

    easing = Raphael.easing_formulas[ easing || 'linear' ];
    duration = duration === undefined ? 250 : duration;

    if (canvas.currentViewBox == undefined) {
        canvas.currentViewBox = {
            x: 0,
            y: 0,
            width: this._viewBox ? this._viewBox[2] : canvas.width,
            height: this._viewBox ? this._viewBox[3] : canvas.height 
        }
    }

    function step(timestamp) {
        var progress = timestamp - start;
        var progressFraction = progress/duration;

        if (progressFraction > 1) {
            progressFraction = 1;
        }

        var easedProgress = easing( progressFraction );
        var newViewBox = {
            x: canvas.currentViewBox.x + (viewX - canvas.currentViewBox.x) * easedProgress,
            y: canvas.currentViewBox.y + (viewY - canvas.currentViewBox.y) * easedProgress,
            width: canvas.currentViewBox.width + (width - canvas.currentViewBox.width) * easedProgress,
            height: canvas.currentViewBox.height + (height - canvas.currentViewBox.height) * easedProgress
        };
        canvas.setViewBox(newViewBox.x, newViewBox.y, newViewBox.width, newViewBox.height, false);

        if (progressFraction < 1) {
            window.requestAnimationFrame(step);
        } else {
            canvas.currentViewBox = newViewBox;
            if (callback) {
                callback(newViewBox.x, newViewBox.y, newViewBox.width, newViewBox.height);
            }
        }
    }
    window.requestAnimationFrame(step);
}

+1 对我来说,这是此页面中动画最流畅的。初始版本会因为画布设置为百分比宽度或高度(例如“'100%'”)而出现一堆NaN。我进行了编辑,如果可用this._viewBox(应该可用),则可以防止出现此问题。 - user56reinstatemonica8

2

对于Patrics的答案进行小修改以去掉“currentViewBox”...这适用于Raphael 2.1.0:

Raphael.fn.animateViewBox = function(viewX, viewY, width, height, duration, callback) {

    duration = duration || 250;

    //current viewBox Data from where the animation should start
    var originals = {
            x: this._viewBox[0], 
            y: this._viewBox[1], 
            width: this._viewBox[2], 
            height: this._viewBox[3]
    }, 
    differences = {
            x: viewX - originals.x,
            y: viewY - originals.y,
            width: width - originals.width,
            height: height - originals.height
    },
    delay = 13,
    stepsNum = Math.ceil(duration / delay),
    stepped = {
            x: differences.x / stepsNum,
            y: differences.y / stepsNum,
            width: differences.width / stepsNum,
            height: differences.height / stepsNum
    }, i,
    canvas = this;

    /**
     * Using a lambda to protect a variable with its own scope.
     * Otherwise, the variable would be incremented inside the loop, but its
     * final value would be read at run time in the future.
     */
    function timerFn(iterator) {
        return function() {
                canvas.setViewBox(
                        originals.x + (stepped.x * iterator),
                        originals.y + (stepped.y * iterator),
                        originals.width + (stepped.width * iterator),
                        originals.height + (stepped.height * iterator),
                        true
                );
                // Run the callback as soon as possible, in sync with the last step
                if(iterator == stepsNum && callback) {
                        callback(viewX, viewY, width, height);
                }
        }
    }

    // Schedule each animation step in to the future
    // Todo: use some nice easing
    for(i = 1; i <= stepsNum; ++i) {
        setTimeout(timerFn(i), i * delay);
    }
} 

如果发布一个模块是不好的礼节,我很抱歉。请随意将其合并到patrics的版本中,但是这样评论就没有意义了。


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