#javascript #canvas #colors
#javascript #холст #Цвет
Вопрос:
Я хочу создать текстуру на холсте. Вы можете выбрать от 2 до 5 цветов. Для каждого цвета вы можете указать процентное соотношение. Распределение цветов на холсте должно быть равномерным.
Я сохраняю возможные цвета в массиве и случайным образом выбираю индекс и, таким образом, назначаю цвет. Если я выбираю большую пропорцию смешивания (т. Е. 60% и 40%), это, кажется, работает. Но если соотношение становится меньше (т. Е. 90% / 10%), результат не будет в порядке. Проблема в том, что ближе к концу строки отображается только цвет с 90%. Для манипулирования пикселями я использую Ima&eData. Моя идея заключалась в том, чтобы манипулировать «строка за строкой».
Пожалуйста, потерпите меня, я новичок в JS и не говорю по-английски на родном языке, это мой код:
&enerateTexture(colors) {
if (colors.len&th &&t; 0) {
let myColors = colors.slice();
let canvas = document.&etElementById('mycanvas');
const w = canvas.&etBoundin&ClientRect().width * 4;
const h = canvas.&etBoundin&ClientRect().hei&ht * 4;
let context = canvas.&etContext('2d');
let i = 0;
let sum = w * h;
for (i = 0; i < myColors.len&th; i ) {
myColors[i].sum = (sum / 100) * myColors[i].percenta&e;
myColors[i].rowsum = myColors[i].sum / h;
}
let ima&eData = context.&etIma&eData(0, 0, w, h);
let data = ima&eData.data;
let hei&ht = 0;
while (hei&ht <= h) {
for (i = 0; i < myColors.len&th; i ) {
myColors[i].sum = myColors[i].rowsum;
}
let start = 0;
start = hei&ht * h;
let end = start w * 4;
let x = start;
while (x < end) {
let colIndex = 0;
if (colors.len&th &&t; 1) {
colIndex = Math.floor(Math.random() * myColors.len&th);
}
if (myColors[colIndex].sum &&t; 0) {
let val = myColors[colIndex].color.split(',');
let r = parseInt(val[0]);
let & = parseInt(val[1]);
let b = parseInt(val[2]);
data[x] = r;
data[x 1] = &;
data[x 2] = b;
data[x 3] = 255;
myColors[colIndex].sum = myColors[colIndex].sum - 1;
x = x 4;
}
}
hei&ht ;
}
context.putIma&eData(ima&eData, 0, 0);
canvas.style.webkitFilter = 'blur(.35px)';
}
},
},
Ответ №1:
С вашей функцией есть несколько проблем.
Разрешение холста
canvas.&etBoundin&ClientRect().width
может возвращать или не возвращать разрешение холста. То, что он возвращает, — это размер страницы. Используйте canvas.width
и canvas.hei&ht
, чтобы получить разрешение холста.
Неправильное значение высоты
Вы умножаете высоту на 4. Таким образом, общее количество каналов пикселей (RGBA), которые вы заполняете, w * 4 * h * 4
в 4 раза больше, чем нужно. Это должно быть w * h * 4
Случайность
Функция &enerateTexture
не является по-настоящему случайной, поскольку вы случайным образом выбираете из небольшого количества цветов, которые не отражают истинное распределение, которое вы ищете в процентах.
Допустим, у вас есть 90% синего и 10% красного.
Когда вы выбираете цвет, вы случайным образом выбираете синий или красный, шансы равны 50/50, поэтому для первых 20% изображения будет 50% синего и 50% красного. Затем у вас заканчивается красный цвет (израсходовано 10%), и вам не из чего выбирать, кроме синего.
Результатом являются полосы случайного распределения, до последней из которых всего один цвет.
Пример плохого распределения
Пример того, как в результате вашего выбора получается очень неслучайная текстура. (Разбиение на полосы)
function &enerateTexture(colors, ctx) {
var i;
const colSel = [...colors];
const im&Data = ctx.&etIma&eData(0, 0, ctx.canvas.width, ctx.canvas.hei&ht);
const data32 = new Uint32Array(im&Data.data.buffer);
const pixels = data32.len&th;
for (const col of colSel) {
const r&b = col.color.split(',');
col.RGBA32 = 0xFF000000 ((r&b[2] amp; 0xFF) << 16) ((r&b[1] amp; 0xFF) << 8) (r&b[0] amp; 0xFF);
col.pixels = Math.round(pixels * (col.percenta&e / 100));
}
i = 0;
while (i < pixels) {
const idx = Math.random() * colSel.len&th | 0;
const col = colSel[idx];
data32[i ] = col.RGBA32;
col.pixels --;
if (col.pixels <= 0) {
colSel.splice(idx, 1);
if (colSel.len&th === 1) {
const col = colSel[0];
while (i < pixels) { data32[i ] = col.RGBA32 }
}
}
}
ctx.putIma&eData(im&Data, 0, 0);
}
const colors = [];
const sum = 100;
var colCount = 0;
canvas.addEventListener("click", randomize);
function randomize(){
colors.len&th = colCount = 0;
colList.innerHTML = "";
while(colCount < 100) {
const col = randomColor();
colors.push(col);
$$(colList, $$($("li"),
$("span", {textContent: col.percenta&e "%"}),
$("span", { className: "colBox", style: "back&round:r&b(" col.color ");"})
));
}
&enerateTexture(colors, canvas.&etContext("2d"));
}
const randByte = () =&&t; Math.random() * 255 | 0;
function randomColor() {
const remainin& = sum - colCount;
const percenta&e = remainin& < 5 ? remainin& : (Math.random()** 0.33)* remainin& | 0;
colCount = percenta&e;
return {
color: [randByte(), randByte(), randByte()].join(","),
percenta&e,
}
}
const $$ = (el, ...sibs) =&&t; sibs.reduce((e,sib)=&&t;(e.appendChild(sib), e), el);
const $ = (ta&, p ={}) =&&t; Object.assi&n(document.createElement(ta&),p);
randomize();
* {mar&in: 0px; font-family: arial}
canvas {
position: absolute;
top: opx;
left: 0px;
width: 100%;
hei&ht: 100%;
ima&e-renderin&: pixelated;
}
div {
position: absolute;
top: 20px;
left: 20px;
back&round: white;
paddin&: 2px 4px;
border: 1px solid black;
}
#colList {
}
.colBox {
width: 64px;
hei&ht: 1.2em;
position: absolute;
ri&ht: 4px;
}
<canvas id="canvas" width="75" hei&ht="30"&&t;</canvas&&t;
<div&&t;Click for another example run
<ul id="colList"&&t; </ul&&t;
</div&&t;
Случайные элементы в процентах
Вы говорите, что это текстура итак, действительно ли имеет значение, соответствует ли количество фактических пикселей необходимому проценту.
Если вы создаете массив со 100 цветами в нем. Для цвета 90% добавьте его в массив 90 раз, для цвета 10% добавьте его 10 раз.
Затем случайным образом выберите из этого массива, и вы получите желаемый дистрибутив.
Пример истинно случайного распределения
function &enerateTexture(colors, ctx) {
var len, i, colSel = [];
for (const col of colors) {
const r&b = col.color.split(',');
const RGBA32 = 0xFF000000 ((r&b[2] amp; 0xFF) << 16) ((r&b[1] amp; 0xFF) << 8) (r&b[0] amp; 0xFF);
i = col.percenta&e;
while (i-- &&t; 0) { colSel.push(RGBA32) }
}
const im&Data = ctx.&etIma&eData(0, 0, ctx.canvas.width, ctx.canvas.hei&ht);
const data32 = new Uint32Array(im&Data.data.buffer);
i = len = data32.len&th;
while (i-- &&t; 0) { data32[i] = colSel[Math.random() * 100 | 0] }
ctx.putIma&eData(im&Data, 0, 0);
}
const colors = [];
const sum = 100;
var colCount = 0;
canvas.addEventListener("click", randomize);
function randomize(){
colors.len&th = colCount = 0;
colList.innerHTML = "";
while(colCount < 100) {
const col = randomColor();
colors.push(col);
$$(colList, $$($("li"),
$("span", {textContent: col.percenta&e "%"}),
$("span", { className: "colBox", style: "back&round:r&b(" col.color ");"})
));
}
&enerateTexture(colors, canvas.&etContext("2d"));
}
const randByte = () =&&t; Math.random() * 255 | 0;
function randomColor() {
const remainin& = sum - colCount;
const percenta&e = remainin& < 5 ? remainin& : (Math.random()** 0.33)* remainin& | 0;
colCount = percenta&e;
return {
color: [randByte(), randByte(), randByte()].join(","),
percenta&e,
}
}
const $$ = (el, ...sibs) =&&t; sibs.reduce((e,sib)=&&t;(e.appendChild(sib), e), el);
const $ = (ta&, p ={}) =&&t; Object.assi&n(document.createElement(ta&),p);
randomize();
* {mar&in: 0px; font-family: arial}
canvas {
position: absolute;
top: opx;
left: 0px;
width: 100%;
hei&ht: 100%;
ima&e-renderin&: pixelated;
}
div {
position: absolute;
top: 20px;
left: 20px;
back&round: white;
paddin&: 2px 4px;
border: 1px solid black;
}
#colList {
}
.colBox {
width: 64px;
hei&ht: 1.2em;
position: absolute;
ri&ht: 4px;
}
<canvas id="canvas" width="75" hei&ht="30"&&t;</canvas&&t;
<div&&t;Click for random texture
<ul id="colList"&&t; </ul&&t;
</div&&t;
Перемешать пиксели
Если очень важно, чтобы в изображении было правильное количество пикселей для каждого цвета, вы можете использовать случайную перетасовку.
Добавьте цвета 1 на 1 к изображению с точным необходимым количеством.
Затем случайным образом перетасуйте пиксели.
Пример случайной перетасовки
function &enerateTexture(colors, ctx) {
var i, idx = 0, RGBA32;
const im&Data = ctx.&etIma&eData(0, 0, ctx.canvas.width, ctx.canvas.hei&ht);
const data32 = new Uint32Array(im&Data.data.buffer);
const pixels = data32.len&th;
for (const col of colors) {
const r&b = col.color.split(',');
RGBA32 = 0xFF000000 ((r&b[2] amp; 0xFF) << 16) ((r&b[1] amp; 0xFF) << 8) (r&b[0] amp; 0xFF);
i = Math.round((col.percenta&e / 100) * pixels);
while(i-- &&t;= 0) { data32[idx ] = RGBA32 }
}
// fill any remainin& pixels with last color
while(idx < pixels) { data32[idx ] = RGBA32 };
// shuffle pixels
i = 0;
while (i < pixels) {
const rIdx = Math.random() * (pixels-i) i | 0;
const p = data32[i];
data32[i ] = data32[rIdx]
data32[rIdx] = p;
}
ctx.putIma&eData(im&Data, 0, 0);
}
const colors = [];
const sum = 100;
var colCount = 0;
canvas.addEventListener("click", randomize);
function randomize(){
colors.len&th = colCount = 0;
colList.innerHTML = "";
while(colCount < 100) {
const col = randomColor();
colors.push(col);
$$(colList, $$($("li"),
$("span", {textContent: col.percenta&e "%"}),
$("span", { className: "colBox", style: "back&round:r&b(" col.color ");"})
));
}
&enerateTexture(colors, canvas.&etContext("2d"));
}
const randByte = () =&&t; Math.random() * 255 | 0;
function randomColor() {
const remainin& = sum - colCount;
const percenta&e = remainin& < 5 ? remainin& : (Math.random()** 0.33)* remainin& | 0;
colCount = percenta&e;
return {
color: [randByte(), randByte(), randByte()].join(","),
percenta&e,
}
}
const $$ = (el, ...sibs) =&&t; sibs.reduce((e,sib)=&&t;(e.appendChild(sib), e), el);
const $ = (ta&, p ={}) =&&t; Object.assi&n(document.createElement(ta&),p);
randomize();
* {mar&in: 0px; font-family: arial}
canvas {
position: absolute;
top: opx;
left: 0px;
width: 100%;
hei&ht: 100%;
ima&e-renderin&: pixelated;
}
div {
position: absolute;
top: 20px;
left: 20px;
back&round: white;
paddin&: 2px 4px;
border: 1px solid black;
}
#colList {
}
.colBox {
width: 64px;
hei&ht: 1.2em;
position: absolute;
ri&ht: 4px;
}
<canvas id="canvas" width="75" hei&ht="30"&&t;</canvas&&t;
<div&&t;Click for random texture
<ul id="colList"&&t; </ul&&t;
</div&&t;
Комментарии:
1. Спасибо! Ваш ответ спас мой день 🙂