javascript - 在画布上创建纹理
问题描述
我想在画布上创建纹理。您可以选择 2 到 5 种颜色。对于每种颜色,您可以指定百分比。画布上的颜色分布应该是均匀的。
我将可能的颜色保存在一个数组中并随机选择索引并分配颜色。如果我选择一个大的混合比例(即 60% 和 40%),它似乎可以工作。但是如果比例变小(即90%/10%),结果就不行了。问题是在行尾只出现 90% 的颜色。对于像素操作,我使用 ImageData。我的想法是“逐行”操作。
请多多包涵,我是 JS 初学者,不会说英语母语这是我的代码:
generateTexture(colors) {
if (colors.length > 0) {
let myColors = colors.slice();
let canvas = document.getElementById('mycanvas');
const w = canvas.getBoundingClientRect().width * 4;
const h = canvas.getBoundingClientRect().height * 4;
let context = canvas.getContext('2d');
let i = 0;
let sum = w * h;
for (i = 0; i < myColors.length; i++) {
myColors[i].sum = (sum / 100) * myColors[i].percentage;
myColors[i].rowsum = myColors[i].sum / h;
}
let imageData = context.getImageData(0, 0, w, h);
let data = imageData.data;
let height = 0;
while (height <= h) {
for (i = 0; i < myColors.length; i++) {
myColors[i].sum = myColors[i].rowsum;
}
let start = 0;
start = height * h;
let end = start + w * 4;
let x = start;
while (x < end) {
let colIndex = 0;
if (colors.length > 1) {
colIndex = Math.floor(Math.random() * myColors.length);
}
if (myColors[colIndex].sum > 0) {
let val = myColors[colIndex].color.split(',');
let r = parseInt(val[0]);
let g = parseInt(val[1]);
let b = parseInt(val[2]);
data[x] = r;
data[x + 1] = g;
data[x + 2] = b;
data[x + 3] = 255;
myColors[colIndex].sum = myColors[colIndex].sum - 1;
x = x + 4;
}
}
height++;
}
context.putImageData(imageData, 0, 0);
canvas.style.webkitFilter = 'blur(.35px)';
}
},
},
解决方案
您的功能有几个问题。
画布分辨率
canvas.getBoundingClientRect().width
可能会或可能不会返回画布分辨率。它返回的是页面上的大小。使用canvas.width
和canvas.height
获取画布的分辨率。
错误的高度值
您将高度乘以 4。因此,您要填充的像素通道 (RGBA) 的总数是w * 4 * h * 4
4 倍。它应该是w * h * 4
随机性
该函数generateTexture
并不是真正随机的,因为您是从少数颜色中随机挑选的,这些颜色并不代表您所追求的真实分布与百分比。
假设你有 90% 的蓝色和 10% 的红色。
当您选择一种颜色时,您会随机选择蓝色或红色,50/50 的偶数几率,因此对于图像的前 20%,它将是 50% 的蓝色和 50% 的红色。然后你的红色用完了(用完 10%),除了蓝色你别无选择。
结果是随机分布的条带,直到最后只有一种颜色。
分布不良的例子
您的选择如何导致非常非随机纹理的示例。(带状)
function generateTexture(colors, ctx) {
var i;
const colSel = [...colors];
const imgData = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height);
const data32 = new Uint32Array(imgData.data.buffer);
const pixels = data32.length;
for (const col of colSel) {
const rgb = col.color.split(',');
col.RGBA32 = 0xFF000000 + ((rgb[2] & 0xFF) << 16) + ((rgb[1] & 0xFF) << 8) + (rgb[0] & 0xFF);
col.pixels = Math.round(pixels * (col.percentage / 100));
}
i = 0;
while (i < pixels) {
const idx = Math.random() * colSel.length | 0;
const col = colSel[idx];
data32[i++] = col.RGBA32;
col.pixels --;
if (col.pixels <= 0) {
colSel.splice(idx, 1);
if (colSel.length === 1) {
const col = colSel[0];
while (i < pixels) { data32[i++] = col.RGBA32 }
}
}
}
ctx.putImageData(imgData, 0, 0);
}
const colors = [];
const sum = 100;
var colCount = 0;
canvas.addEventListener("click", randomize);
function randomize(){
colors.length = colCount = 0;
colList.innerHTML = "";
while(colCount < 100) {
const col = randomColor();
colors.push(col);
$$(colList, $$($("li"),
$("span", {textContent: col.percentage + "%"}),
$("span", { className: "colBox", style: "background:rgb("+col.color+ ");"})
));
}
generateTexture(colors, canvas.getContext("2d"));
}
const randByte = () => Math.random() * 255 | 0;
function randomColor() {
const remaining = sum - colCount;
const percentage = remaining < 5 ? remaining : (Math.random()** 0.33)* remaining | 0;
colCount += percentage;
return {
color: [randByte(), randByte(), randByte()].join(","),
percentage,
}
}
const $$ = (el, ...sibs) => sibs.reduce((e,sib)=>(e.appendChild(sib), e), el);
const $ = (tag, p ={}) => Object.assign(document.createElement(tag),p);
randomize();
* {margin: 0px; font-family: arial}
canvas {
position: absolute;
top: opx;
left: 0px;
width: 100%;
height: 100%;
image-rendering: pixelated;
}
div {
position: absolute;
top: 20px;
left: 20px;
background: white;
padding: 2px 4px;
border: 1px solid black;
}
#colList {
}
.colBox {
width: 64px;
height: 1.2em;
position: absolute;
right: 4px;
}
<canvas id="canvas" width="75" height="30"></canvas>
<div>Click for another example run
<ul id="colList"> </ul>
</div>
按百分比的随机项
您说这是纹理,那么实际像素的数量是否与所需的百分比匹配真的很重要。
如果您创建一个包含 100 种颜色的数组。对于 90% 的颜色,将其添加到数组中 90 次,对于 10% 的颜色,将其添加 10 次。
然后从该数组中随机选择,您将获得所需的分布。
真实随机分布示例
function generateTexture(colors, ctx) {
var len, i, colSel = [];
for (const col of colors) {
const rgb = col.color.split(',');
const RGBA32 = 0xFF000000 + ((rgb[2] & 0xFF) << 16) + ((rgb[1] & 0xFF) << 8) + (rgb[0] & 0xFF);
i = col.percentage;
while (i-- > 0) { colSel.push(RGBA32) }
}
const imgData = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height);
const data32 = new Uint32Array(imgData.data.buffer);
i = len = data32.length;
while (i-- > 0) { data32[i] = colSel[Math.random() * 100 | 0] }
ctx.putImageData(imgData, 0, 0);
}
const colors = [];
const sum = 100;
var colCount = 0;
canvas.addEventListener("click", randomize);
function randomize(){
colors.length = colCount = 0;
colList.innerHTML = "";
while(colCount < 100) {
const col = randomColor();
colors.push(col);
$$(colList, $$($("li"),
$("span", {textContent: col.percentage + "%"}),
$("span", { className: "colBox", style: "background:rgb("+col.color+ ");"})
));
}
generateTexture(colors, canvas.getContext("2d"));
}
const randByte = () => Math.random() * 255 | 0;
function randomColor() {
const remaining = sum - colCount;
const percentage = remaining < 5 ? remaining : (Math.random()** 0.33)* remaining | 0;
colCount += percentage;
return {
color: [randByte(), randByte(), randByte()].join(","),
percentage,
}
}
const $$ = (el, ...sibs) => sibs.reduce((e,sib)=>(e.appendChild(sib), e), el);
const $ = (tag, p ={}) => Object.assign(document.createElement(tag),p);
randomize();
* {margin: 0px; font-family: arial}
canvas {
position: absolute;
top: opx;
left: 0px;
width: 100%;
height: 100%;
image-rendering: pixelated;
}
div {
position: absolute;
top: 20px;
left: 20px;
background: white;
padding: 2px 4px;
border: 1px solid black;
}
#colList {
}
.colBox {
width: 64px;
height: 1.2em;
position: absolute;
right: 4px;
}
<canvas id="canvas" width="75" height="30"></canvas>
<div>Click for random texture
<ul id="colList"> </ul>
</div>
随机播放像素
如果图像中每种颜色的正确像素数非常重要,那么您可以使用随机随机播放。
将颜色 1 x 1 添加到图像中,以达到所需的确切计数。
然后随机打乱像素。
示例随机洗牌
function generateTexture(colors, ctx) {
var i, idx = 0, RGBA32;
const imgData = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height);
const data32 = new Uint32Array(imgData.data.buffer);
const pixels = data32.length;
for (const col of colors) {
const rgb = col.color.split(',');
RGBA32 = 0xFF000000 + ((rgb[2] & 0xFF) << 16) + ((rgb[1] & 0xFF) << 8) + (rgb[0] & 0xFF);
i = Math.round((col.percentage / 100) * pixels);
while(i-- >= 0) { data32[idx++] = RGBA32 }
}
// fill any remaining 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.putImageData(imgData, 0, 0);
}
const colors = [];
const sum = 100;
var colCount = 0;
canvas.addEventListener("click", randomize);
function randomize(){
colors.length = colCount = 0;
colList.innerHTML = "";
while(colCount < 100) {
const col = randomColor();
colors.push(col);
$$(colList, $$($("li"),
$("span", {textContent: col.percentage + "%"}),
$("span", { className: "colBox", style: "background:rgb("+col.color+ ");"})
));
}
generateTexture(colors, canvas.getContext("2d"));
}
const randByte = () => Math.random() * 255 | 0;
function randomColor() {
const remaining = sum - colCount;
const percentage = remaining < 5 ? remaining : (Math.random()** 0.33)* remaining | 0;
colCount += percentage;
return {
color: [randByte(), randByte(), randByte()].join(","),
percentage,
}
}
const $$ = (el, ...sibs) => sibs.reduce((e,sib)=>(e.appendChild(sib), e), el);
const $ = (tag, p ={}) => Object.assign(document.createElement(tag),p);
randomize();
* {margin: 0px; font-family: arial}
canvas {
position: absolute;
top: opx;
left: 0px;
width: 100%;
height: 100%;
image-rendering: pixelated;
}
div {
position: absolute;
top: 20px;
left: 20px;
background: white;
padding: 2px 4px;
border: 1px solid black;
}
#colList {
}
.colBox {
width: 64px;
height: 1.2em;
position: absolute;
right: 4px;
}
<canvas id="canvas" width="75" height="30"></canvas>
<div>Click for random texture
<ul id="colList"> </ul>
</div>
推荐阅读
- haskell - 新类型的 Haskell Monoid 实例问题
- acumatica - 是否可以在 Acumatica 中使用 SOAP API 导出分支列表
- this - 我并没有真正得到构造函数方法
- python - sklearn train_test_split:当我们得到:“目标是多类但平均值='二进制'......错误时,在哪里添加平均值=无?
- javascript - 我想让我的不和谐机器人状态改变
- wordpress - 如何仅在 wordpress 博客的主页上显示智能滑块
- django - 为什么它在图像源之前注入“/product/image-name/”
- azure-ad-b2c - 消费层和 Azure AD B2C 中的 Azure API 管理
- vue.js - 如何将Vuetify select的值设置为数组或对象
- memory - 为什么我的显卡会以这种特殊模式出现故障?