Рафаэлевая анимация масштабирования бумаги
Мне удалось немного взломать, чтобы увеличить изображение 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));
}
где элемент 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);
Возможно ли преобразовать функцию, чтобы она плавно менялась?
Ответы
Ответ 1
См. этот пример JS Fiddle для использования jQuery и Raphael setViewBox, который обеспечивает плавное масштабирование и панорамирование. Мы используем ту же функциональность в гораздо более сложном и большем контексте, и она отлично работает и остается гладкой даже при большом количестве элементов, нарисованных на экране.
В принципе, не пытайтесь изобретать колесо. Я знаю, что это не тот ответ, который вы ищете, но это почти наверняка ваш лучший вариант.
EDIT:
Я исправил прикрепленную скрипку (она была повреждена из-за изменений, сделанных JSFiddle на сайт), но, судя по всему, SO не позволит мне сохранять ссылки JSFiddle без включения какого-либо кода, поэтому...
console.log("hello world!");
Ответ 2
Для кого-то (такого как я), который хотел, чтобы масштабирование и панорамирование плавно анимированные, смотрите здесь:
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);
}
}
Ответ 3
Незначительный mod to 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, но тогда комментарии не имеют смысла.
Ответ 4
Как и выше, но с различиями:
- Не зависит от внутренних компонентов (которые с тех пор были изменены)
- Не требует какой-либо глобальной настройки (она обрабатывается для вас)
- Использует requestAnimationFrame, что приводит к гораздо более плавным анимациям (но не работает в IE9 или ниже)
- Использует Функции ускорения Raphael (
linear
, easeIn
, easeOut
, easeInOut
, backIn
, backOut
, elastic
, bounce
)
Так как это полная перепись, я не модифицировал выше.
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);
}