如何渲染数以万计的元素?
How can I render tens of thousands of elements?
在你说我疯了之前,请相信我,我知道。我不会选择一个呈现速度快、加载速度快或灯塔得分高的网站。我只是想让它工作。
我有一些 javascript 可以拾取图像的所有像素颜色。使用此函数,我创建了一个 1px x 1px 的 div 元素,并将背景颜色设置为相同坐标的像素颜色。然后坐标用于设置顶部和左侧的值。我的代码按照它所说的去做。
这是我的问题,我的图像是 700 像素 x 387 像素。如果您进行数学计算,结果为 270,900 html 个元素。 Chrome,根本不是为了这种疯狂而建造的。我想看这个作品,我想 "manually" 创建一个包含 div 元素的图像,不知何故。当我尝试这样做时,我的 cpu 达到最大值,我确信我最终会 运行 超出 ram。
如果我只尝试几百或几千像素,一切正常,但再多一点,chrome 就死了。我不确定它是否在浏览器中计算可能是我的问题,或者 chrome 是否无法显示这么多元素,或两者兼而有之。我想我可以在我的服务器上使用 python 进行相同的计算,并将其附加到 html,但是 chrome 可能无法显示它。
显然,这不是特别重要,只是有趣。我认为社区也会享受挑战。
此处计算 100 个像素:
onload = e => {
function componentToHex(c) {
var hex = c.toString(16);
return hex.length == 1 ? "0" + hex : hex;
}
function rgbToHex(r, g, b) {
return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b);
}
function img(x, y) {
var img = document.getElementById('my-image');
var canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
canvas.getContext('2d').drawImage(img, 0, 0, img.width, img.height);
var pixelData = canvas.getContext('2d').getImageData(x, y, 1, 1).data;
return rgbToHex(pixelData[0], pixelData[1], pixelData[2]);
}
//x = 700 y = 387
for (var x = 0; x < 10; x++) {
for (var y = 0; y < 10; y++) {
document.body.insertAdjacentHTML("beforeend", "<div style='top:" + y + "px; left:" + x + "px;background:" + img(x, y) + ";' />");
}
}
};
div {
position: absolute;
width: 1px;
height: 1px;
}
<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/f/fb/New_born_Frisian_red_white_calf.jpg/640px-New_born_Frisian_red_white_calf.jpg" id="my-image" crossorigin="anonymous">
这里正在计算 2500px(仍然有效,需要一段时间)
onload = e => {
function componentToHex(c) {
var hex = c.toString(16);
return hex.length == 1 ? "0" + hex : hex;
}
function rgbToHex(r, g, b) {
return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b);
}
function img(x, y) {
var img = document.getElementById('my-image');
var canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
canvas.getContext('2d').drawImage(img, 0, 0, img.width, img.height);
var pixelData = canvas.getContext('2d').getImageData(x, y, 1, 1).data;
return rgbToHex(pixelData[0], pixelData[1], pixelData[2]);
}
//x = 700 y = 387
for (var x = 0; x < 50; x++) {
for (var y = 0; y < 50; y++) {
document.body.insertAdjacentHTML("beforeend", "<div style='top:" + y + "px; left:" + x + "px;background:" + img(x, y) + ";' />");
}
}
};
div {
position: absolute;
width: 1px;
height: 1px;
}
<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/f/fb/New_born_Frisian_red_white_calf.jpg/640px-New_born_Frisian_red_white_calf.jpg" id="my-image" crossorigin="anonymous">
干杯,艾萨克。
目前,您正在为每个像素执行以下操作
- 创建canvas
- 获取上下文并绘制到图像
- 获取上下文并获取一个像素的像素数据
- 创建 DIV
- 将其添加到 DOM
现在,让我们简化一下
以下可以一次性完成
- 创建 canvas
- 获取上下文
- 使用上下文绘制图像
- 使用上下文获取图像数据
- 创建一个空字符串
现在,对于每个像素
- 获取像素数据
- 将 div 的 html 添加到字符串
最后,只有一次
- 将包含所有 div 的字符串添加到 DOM
类似于:
const componentToHex = c => c.toString(16).padStart(2, '0');
const rgbToHex = (r, g, b) => `#${componentToHex(r)}${componentToHex(g)}${componentToHex(b)}`;
const img = document.getElementById('my-image');
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
const w = canvas.width = img.width;
const h = canvas.height = img.height;
context.drawImage(img, 0, 0, img.width, img.height);
const imageData = context.getImageData(0, 0, w, h).data;
const pixel = (x, y) => {
const index = (w * y + x) * 4;
return rgbToHex(imageData[index], imageData[index + 1], imageData[index + 2]);
}
const df = document.createDocumentFragment();
for (let y = 0; y < img.height; y++) {
for (let x = 0; x < img.width; x++) {
const div = df.appendChild(document.createElement('div'));
div.style.top = y + "px";
div.style.left = x + "px";
div.style.backgroundColor = pixel(x, y);
}
}
document.body.appendChild(df);
注意:现在可能不是这样,但这样的循环在函数内部可能运行得更快——通常在全局上下文中的循环更慢
因此,您可以将上面的整个代码包装在
(() => {
// the code from above
})();
再次看到显着改善 - 不确定,过去几年曾经是这种情况
changed to use document fragment
for a further 25% speed improvement
Now takes 1.4 seconds in firefox for a 640x480 image, 2.3 seconds in chrome - which didn't really see a big difference between using insertAdjacentHTML
vs a document fragment
another thing to note. In Firefox the page becomes sluggish, in chrome, for 640x480, no such issue
在你说我疯了之前,请相信我,我知道。我不会选择一个呈现速度快、加载速度快或灯塔得分高的网站。我只是想让它工作。
我有一些 javascript 可以拾取图像的所有像素颜色。使用此函数,我创建了一个 1px x 1px 的 div 元素,并将背景颜色设置为相同坐标的像素颜色。然后坐标用于设置顶部和左侧的值。我的代码按照它所说的去做。
这是我的问题,我的图像是 700 像素 x 387 像素。如果您进行数学计算,结果为 270,900 html 个元素。 Chrome,根本不是为了这种疯狂而建造的。我想看这个作品,我想 "manually" 创建一个包含 div 元素的图像,不知何故。当我尝试这样做时,我的 cpu 达到最大值,我确信我最终会 运行 超出 ram。
如果我只尝试几百或几千像素,一切正常,但再多一点,chrome 就死了。我不确定它是否在浏览器中计算可能是我的问题,或者 chrome 是否无法显示这么多元素,或两者兼而有之。我想我可以在我的服务器上使用 python 进行相同的计算,并将其附加到 html,但是 chrome 可能无法显示它。
显然,这不是特别重要,只是有趣。我认为社区也会享受挑战。
此处计算 100 个像素:
onload = e => {
function componentToHex(c) {
var hex = c.toString(16);
return hex.length == 1 ? "0" + hex : hex;
}
function rgbToHex(r, g, b) {
return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b);
}
function img(x, y) {
var img = document.getElementById('my-image');
var canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
canvas.getContext('2d').drawImage(img, 0, 0, img.width, img.height);
var pixelData = canvas.getContext('2d').getImageData(x, y, 1, 1).data;
return rgbToHex(pixelData[0], pixelData[1], pixelData[2]);
}
//x = 700 y = 387
for (var x = 0; x < 10; x++) {
for (var y = 0; y < 10; y++) {
document.body.insertAdjacentHTML("beforeend", "<div style='top:" + y + "px; left:" + x + "px;background:" + img(x, y) + ";' />");
}
}
};
div {
position: absolute;
width: 1px;
height: 1px;
}
<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/f/fb/New_born_Frisian_red_white_calf.jpg/640px-New_born_Frisian_red_white_calf.jpg" id="my-image" crossorigin="anonymous">
这里正在计算 2500px(仍然有效,需要一段时间)
onload = e => {
function componentToHex(c) {
var hex = c.toString(16);
return hex.length == 1 ? "0" + hex : hex;
}
function rgbToHex(r, g, b) {
return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b);
}
function img(x, y) {
var img = document.getElementById('my-image');
var canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
canvas.getContext('2d').drawImage(img, 0, 0, img.width, img.height);
var pixelData = canvas.getContext('2d').getImageData(x, y, 1, 1).data;
return rgbToHex(pixelData[0], pixelData[1], pixelData[2]);
}
//x = 700 y = 387
for (var x = 0; x < 50; x++) {
for (var y = 0; y < 50; y++) {
document.body.insertAdjacentHTML("beforeend", "<div style='top:" + y + "px; left:" + x + "px;background:" + img(x, y) + ";' />");
}
}
};
div {
position: absolute;
width: 1px;
height: 1px;
}
<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/f/fb/New_born_Frisian_red_white_calf.jpg/640px-New_born_Frisian_red_white_calf.jpg" id="my-image" crossorigin="anonymous">
干杯,艾萨克。
目前,您正在为每个像素执行以下操作
- 创建canvas
- 获取上下文并绘制到图像
- 获取上下文并获取一个像素的像素数据
- 创建 DIV
- 将其添加到 DOM
现在,让我们简化一下
以下可以一次性完成
- 创建 canvas
- 获取上下文
- 使用上下文绘制图像
- 使用上下文获取图像数据
- 创建一个空字符串
现在,对于每个像素
- 获取像素数据
- 将 div 的 html 添加到字符串
最后,只有一次
- 将包含所有 div 的字符串添加到 DOM
类似于:
const componentToHex = c => c.toString(16).padStart(2, '0');
const rgbToHex = (r, g, b) => `#${componentToHex(r)}${componentToHex(g)}${componentToHex(b)}`;
const img = document.getElementById('my-image');
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
const w = canvas.width = img.width;
const h = canvas.height = img.height;
context.drawImage(img, 0, 0, img.width, img.height);
const imageData = context.getImageData(0, 0, w, h).data;
const pixel = (x, y) => {
const index = (w * y + x) * 4;
return rgbToHex(imageData[index], imageData[index + 1], imageData[index + 2]);
}
const df = document.createDocumentFragment();
for (let y = 0; y < img.height; y++) {
for (let x = 0; x < img.width; x++) {
const div = df.appendChild(document.createElement('div'));
div.style.top = y + "px";
div.style.left = x + "px";
div.style.backgroundColor = pixel(x, y);
}
}
document.body.appendChild(df);
注意:现在可能不是这样,但这样的循环在函数内部可能运行得更快——通常在全局上下文中的循环更慢
因此,您可以将上面的整个代码包装在
(() => {
// the code from above
})();
再次看到显着改善 - 不确定,过去几年曾经是这种情况
changed to use
document fragment
for a further 25% speed improvement
Now takes 1.4 seconds in firefox for a 640x480 image, 2.3 seconds in chrome - which didn't really see a big difference between usinginsertAdjacentHTML
vs a document fragmentanother thing to note. In Firefox the page becomes sluggish, in chrome, for 640x480, no such issue