我正在我的网站上制作万花筒。它所做的只是拍摄一张图像(通过拖放和拖放或加载时的默认图像)并复制10次(每个万花筒片一个)。在鼠标移动时,调整切片的旋转和比例以达到预期效果。
在Google Chrome和Firefox上,它无缝地工作,没有任何延迟。但是,在Safari上,网站无法使用,因为它太慢了。我错过了什么吗?
这是一个显示问题的JSFiddle。请注意我已经尝试用RequestAnimationFrame替换setTimeout(update,1000/60),没有任何改进。
JSFiddle:Link
$(document).ready(function () {
//SCRIPT KALEIDOSCOPE BASE
var DragDrop, Kaleidoscope, c, dragger, gui, i, image, kaleidoscope, len, onChange, onMouseMoved, options, ref, tr, tx, ty, update,
bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
Kaleidoscope = (function() {
Kaleidoscope.prototype.HALF_PI = Math.PI / 2;
Kaleidoscope.prototype.TWO_PI = Math.PI * 2;
var optimal_radius = window.innerHeight;
if (window.innerWidth > optimal_radius) {
optimal_radius = window.innerWidth;
}
function Kaleidoscope(options1) {
var key, ref, ref1, val;
this.options = options1 != null ? options1 : {};
this.defaults = {
offsetRotation: 0.0,
offsetScale: 1.0,
offsetX: 0.0,
offsetY: 0.0,
radius: optimal_radius / 1.4,
slices: 12,
zoom: 1.0
};
ref = this.defaults;
for (key in ref) {
val = ref[key];
this[key] = val;
}
ref1 = this.options;
for (key in ref1) {
val = ref1[key];
this[key] = val;
}
if (this.domElement == null) {
this.domElement = document.getElementById('kaleidoscope');
}
if (this.context == null) {
this.context = this.domElement.getContext('2d');
}
if (this.image == null) {
this.image = document.createElement('img');
}
}
Kaleidoscope.prototype.draw = function() {
var cx, i, index, ref, results, scale, step;
this.domElement.width = this.domElement.height = this.radius * 2;
this.context.fillStyle = this.context.createPattern(this.image, 'repeat');
scale = this.zoom * (this.radius / Math.min(this.image.width, this.image.height));
step = this.TWO_PI / this.slices;
cx = this.image.width / 2;
results = [];
for (index = i = 0, ref = this.slices; 0 <= ref ? i <= ref : i >= ref; index = 0 <= ref ? ++i : --i) {
this.context.save();
this.context.translate(this.radius, this.radius);
this.context.rotate(index * step);
this.context.beginPath();
this.context.moveTo(-0.5, -0.5);
this.context.arc(0, 0, this.radius, step * -0.51, step * 0.51);
this.context.lineTo(0.5, 0.5);
this.context.closePath();
this.context.rotate(this.HALF_PI);
this.context.scale(scale, scale);
this.context.scale([-1, 1][index % 2], 1);
this.context.translate(this.offsetX - cx, this.offsetY);
this.context.rotate(this.offsetRotation);
this.context.scale(this.offsetScale, this.offsetScale);
this.context.fill();
results.push(this.context.restore());
}
return results;
};
return Kaleidoscope;
})();
DragDrop = (function() {
function DragDrop(callback, context, filter) {
var disable;
this.callback = callback;
this.context = context != null ? context : document;
this.filter = filter != null ? filter : /^image/i;
this.onDrop = bind(this.onDrop, this);
disable = function(event) {
event.stopPropagation();
return event.preventDefault();
};
this.context.addEventListener('dragleave', disable);
this.context.addEventListener('dragenter', disable);
this.context.addEventListener('dragover', disable);
this.context.addEventListener('drop', this.onDrop, false);
}
DragDrop.prototype.onDrop = function(event) {
var file, reader;
event.stopPropagation();
event.preventDefault();
file = event.dataTransfer.files[0];
if (this.filter.test(file.type)) {
reader = new FileReader;
reader.onload = (function(_this) {
return function(event) {
return typeof _this.callback === "function" ? _this.callback(event.target.result) : void 0;
};
})(this);
return reader.readAsDataURL(file);
}
};
return DragDrop;
})();
image = new Image;
image.onload = (function(_this) {
return function() {
return kaleidoscope.draw();
};
})(this);
image.src = 'img/kaleidoscope.jpg';
kaleidoscope = new Kaleidoscope({
image: image,
slices: 10
});
kaleidoscope.domElement.style.position = 'absolute';
kaleidoscope.domElement.style.marginLeft = -kaleidoscope.radius + 'px';
kaleidoscope.domElement.style.marginTop = -kaleidoscope.radius + 'px';
kaleidoscope.domElement.style.left = '50%';
kaleidoscope.domElement.style.top = '50%';
document.getElementsByTagName('header')[0].appendChild(kaleidoscope.domElement);
dragger = new DragDrop(function(data) {
return kaleidoscope.image.src = data;
});
tx = kaleidoscope.offsetX;
ty = kaleidoscope.offsetY;
tr = kaleidoscope.offsetRotation;
onMouseMoved = (function(_this) {
return function(event) {
var cx, cy, dx, dy, hx, hy;
cx = window.innerWidth / 10;
cy = window.innerHeight / 10;
dx = event.pageX / window.innerWidth;
dy = event.pageY / window.innerHeight;
hx = dx - 0.5;
hy = dy - 0.5;
tx = hx * kaleidoscope.radius * -2;
ty = hy * kaleidoscope.radius * 2;
return tr = Math.atan2(hy, hx);
};
})(this);
window.addEventListener('mousemove', onMouseMoved, false);
options = {
interactive: true,
ease: 0.1
};
(update = (function(_this) {
return function() {
var delta, theta;
if (options.interactive) {
delta = tr - kaleidoscope.offsetRotation;
theta = Math.atan2(Math.sin(delta), Math.cos(delta));
kaleidoscope.offsetX += (tx - kaleidoscope.offsetX) * options.ease;
kaleidoscope.offsetY += (ty - kaleidoscope.offsetY) * options.ease;
kaleidoscope.offsetRotation += (theta - kaleidoscope.offsetRotation) * options.ease;
kaleidoscope.draw();
}
return setTimeout(update, 1000 / 60);
};
})(this))();
onChange = (function(_this) {
return function() {
kaleidoscope.domElement.style.marginLeft = -kaleidoscope.radius + 'px';
kaleidoscope.domElement.style.marginTop = -kaleidoscope.radius + 'px';
options.interactive = false;
return kaleidoscope.draw();
};
})(this);
});
从我看到的情况来看,只有在画布全屏时才会出现问题。如果它出现在一个狭小的空间内,它可以无缝地工作。但是,在我的网站上,它将是全屏的。
答案 0 :(得分:8)
哇!你遇到的主要问题是你正在绘制一个巨大的画布。您正在创建比窗口大小更大的画布WAY。尽管未显示画布的一部分,但无论如何都要进行绘制该区域的计算。您只需要绘制可以查看的像素。
在这里,您可以看到实际的画布大小:http://i.imgur.com/trOYlcV.png
通过这个和@Kaiido技巧,我创造了这个小提琴:https://jsfiddle.net/Llorx/sd1skrj8/9/
我的画布尺寸:http://i.imgur.com/4BzmCqh.png
我只是创建了一个填充视口的画布,并在其中绘制增加圆弧半径,画布是限制像素“viewport”的画布,而不是窗口。
更改:
this.context.arc(0, 0, this.radius, step * -0.51, step * 0.51);
// [...]
kaleidoscope.domElement.style.marginLeft = -kaleidoscope.radius + 'px';
kaleidoscope.domElement.style.marginTop = -kaleidoscope.radius + 'px';
kaleidoscope.domElement.style.left = '50%';
kaleidoscope.domElement.style.top = '50%';
的
this.context.arc(0, 0, this.radius*1.5, step * -0.51, step * 0.51);
// [...]
kaleidoscope.domElement.style.width = "100vw";
kaleidoscope.domElement.style.height = "100vh";
kaleidoscope.domElement.style.left = 0;
kaleidoscope.domElement.style.top = 0;
当屏幕比例不是正方形时,这可以改进为具有实际圆圈等等,但是你明白了:永远不要使画布大于需要。
PD:没有Safari可以测试。告诉我这是否会提高性能。
答案 1 :(得分:3)
关于对代码进行的所有优化,以及在safari上它仍然具有接近零的帧率的事实。我尝试修改你使用的图片,减小尺寸(jpg质量60,30,10),更改图像格式(png24,png8),更改图片的大小(250x500而不是750x1500)并且所有这些更改都没有改变。仍然落后很多。
然后我试图找到一些使用Safari Canvas制作的基准测试。我发现这张图表显示Safari与帆布的表现并不是最好的。
您可以查看完整的基准文章here
我认为最终,即使在@JorgeFuentesGonzález进行优化之后,你的代码仍然在Safari上呈现缓慢,那么可能有一个原因,它是Webkit引擎的核心。
答案 2 :(得分:0)
有趣的缺陷,我会删除画布上的css样式,如@Jorge建议的那样,然后我会在屏幕外的画布上渲染效果,然后将渲染的帧复制到可见的画布上。然后,DOM渲染器不需要担心屏幕外剪辑。