拉斐尔纸缩放动画

发布于 2024-12-09 06:35:30 字数 1017 浏览 1 评论 0原文

我设法做了一些 hack 来缩放 raphael paper,因为 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));
} 

其中 viewport1 元素定义为:

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);

是否可以对函数进行改造,使其平滑缩放?

I managed to do some hack in order to zoom raphael paper, as setviewbox wasn't working for me, here is the function I wrote:

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));
} 

where the viewport1 element was defined as :

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

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

Then I can call: paper.zoomAndMove(minx,miny,zoomRatio);

Is it possible to transform the function to make it zoom smoothly?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

野の 2024-12-16 06:35:30

对于任何想要缩放和缩放的人(像我一样)平滑平移动画看看这里:

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

这个片段帮助我自动缩放和平移到画布上的特定点并设置动画。 (支持威尔·摩根

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);
    }

} 

For anybody (like me) who wanted to have the zooming & panning smoothly animated have a look here:

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

this snipped helped me to automate and animate zooming and panning to a specific point on the canvas. (Props to 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);
    }

} 
橘香 2024-12-16 06:35:30

请参阅这个 JS Fiddle 示例,了解如何利用 jQuery 和 Raphael 的 setViewBox,它可以平滑地进行缩放和平移。我们在更复杂和更大的环境中使用完全相同的功能,即使在屏幕上绘制大量项目,它也能完美运行并保持流畅。

基本上,不要尝试重新发明轮子。我知道这可能不是您正在寻找的答案,但它几乎肯定是您的最佳选择。

编辑:
我已经修复了附加的小提琴(由于 JSFiddle 对网站所做的更改而损坏),但显然不允许我在不包含一些代码的情况下保存 JSFiddle 链接,所以...

console.log("hello world!");

See this JS Fiddle example for utilizing jQuery and Raphael's setViewBox which does both zooming and panning smoothly. We use the exact same functionality in a much more complicated and larger context and it works perfect and remains smooth even with a large number of items drawn on screen.

Basically, don't try and re-invent the wheel. I know this may not be the answer you are looking for, but it's almost certainly your best option.

EDIT:
I've fixed the attached fiddle (it was broken due to changes JSFiddle made to the site) but apparently SO won't let me save JSFiddle links without including some code, so...

console.log("hello world!");
内心旳酸楚 2024-12-16 06:35:30

对 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);
    }
} 

如果发布 mod 是不礼貌的行为,请原谅。请随意将其合并到 patrics 的版本中,但这样注释就没有意义了。

Minor mod to patrics' answer to get rid of "currentViewBox"... this works with 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);
    }
} 

Sry if it's bad etiquette to post a mod. Feel free to merge it in patrics' version, but then the comments don't make sense.

分開簡單 2024-12-16 06:35:30

与上面类似,但有区别:

  • 不依赖于内部结构(此后已更改)
  • 不需要任何全局设置(它已为您处理)
  • 使用 requestAnimationFrame,产生更流畅的动画(但 不在 IE9 或更低版本中工作
  • 使用 Raphael 的缓动函数线性easeIneaseOuteaseInOutbackInbackOutelasticbounce

由于这是完全重写,因此我没有修改上面的内容。

 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);
}

Similar to above, but with differences:

  • Doesn't depend on internals (which have since changed)
  • Doesn't require any global set up (it is handled for you)
  • Uses requestAnimationFrame, resulting in much smoother animations (but does not work in IE9 or below)
  • Uses Raphael's easing functions (linear, easeIn, easeOut, easeInOut, backIn, backOut, elastic, bounce)

Since this is a complete rewrite I didn't modify the above.

 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);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文