私が見たコードペンに基づいて、キャンバスにノイズ効果を適用しようとしていますが、これはSO answerに非常に似ているようです。
ランダムに透明なピクセルの「スクリーン」を生成したいのですが、その代わりに完全に不透明な赤のフィールドを取得します。キャンバスまたは型付き配列のどちらかに精通している人が、私が間違っていることを示してくれることを願っています。
(今のところ) ノイズのアニメーション化は気にしないので、codepen コードを大幅にリファクタリングしました。
/**
* apply a "noise filter" to a rectangular region of the canvas
* @param {Canvas2DContext} ctx - the context to draw on
* @param {Number} x - the x-coordinate of the top-left corner of the region to noisify
* @param {Number} y - the y-coordinate of the top-left corner of the region to noisify
* @param {Number} width - how wide, in canvas units, the noisy region should be
* @param {Number} height - how tall, in canvas units, the noisy region should be
* @effect draws directly to the canvas
*/
function drawNoise( ctx, x, y, width, height ) {
let imageData = ctx.createImageData(width, height)
let buffer32 = new Uint32Array(imageData.data.buffer)
for (let i = 0, len = buffer32.length; i < len; i++) {
buffer32[i] = Math.random() < 0.5
? 0x00000088 // "noise" pixel
: 0x00000000 // non-noise pixel
}
ctx.putImageData(imageData, x, y)
}
私が知る限り、起こっていることの核心は、ImageData
の生データ表現 (各ピクセルの赤、緑、青、およびアルファ値を連続して反映する一連の 8 ビット要素)をラップすることです。 32 ビット配列。これにより、各ピクセルを 1 つのタプルとして操作できます。ピクセルあたり 4 つの要素ではなく、ピクセルあたり 1 つの要素を持つ配列を取得します。
次に、その配列内の要素を反復処理し、ノイズ ロジックに基づいて各要素 (つまり、各ピクセル) に RGBA 値を書き込みます。ここでのノイズ ロジックは非常に単純です。各ピクセルが「ノイズ」ピクセルである確率は 50% までです。
ノイズ ピクセルには 32 ビット値 が割り当てられます0x00000088
。これは、(配列によって提供される 32 ビット チャンクのおかげで) rgba(0, 0, 0, 0.5)
、つまり黒、50% の不透明度に相当します。
ノイズのないピクセルには 32 ビット値 が割り当てられます0x00000000
。これは黒の不透明度 0%、つまり完全に透明です。
buffer32
興味深いことに、キャンバスには を書きません。代わりに、 のimageData
構築に使用されたを記述しているためUint32Array
、何らかの参照渡しによって imageData オブジェクトを変更していると思われます。これがなぜなのか正確にはわかりません。値と参照の受け渡しが JS で一般的にどのように機能するかは知っていますが (スカラーは値によって渡され、オブジェクトは参照によって渡されます)、型指定されていない配列の世界では、配列コンストラクターに渡される値が配列の長さを決定するだけです。それは明らかにここで起こっていることではありません。
前述のように、50% または 100% 透明な黒いピクセルのフィールドの代わりに、すべて赤のすべてのベタ ピクセルのフィールドを取得します。赤が表示されるとは思わないだけでなく、ランダムな色の割り当ての証拠はありません。すべてのピクセルが赤一色です。
2 つの 16 進数の値をいじってみると、これにより適切な種類の分布を持つ黒地に赤の散乱が生成されることがわかりました。
buffer32[i] = Math.random() < 0.5
? 0xff0000ff // <-- I'd assume this is solid red
: 0xff000000 // <-- I'd assume this is invisible red
しかし、それはまだ真っ赤で、真っ黒です。基になるキャンバス データは、非表示にする必要があるピクセルを通して表示されません。
紛らわしいことに、赤か黒以外の色を取得できません。また、100% 不透明以外の透明度も得られません。切断を説明するために、ランダム要素を削除し、これらの 9 つの値のそれぞれをすべてのピクセルに書き込んで、何が起こるかを確認しました。
buffer32[i] = 0xRrGgBbAa
// EXPECTED // ACTUAL
buffer32[i] = 0xff0000ff // red 100% // red 100%
buffer32[i] = 0x00ff00ff // green 100% // red 100%
buffer32[i] = 0x0000ffff // blue 100% // red 100%
buffer32[i] = 0xff000088 // red 50% // blood red; could be red on black at 50%
buffer32[i] = 0x00ff0088 // green 50% // red 100%
buffer32[i] = 0x0000ff88 // blue 50% // red 100%
buffer32[i] = 0xff000000 // red 0% // black 100%
buffer32[i] = 0x00ff0000 // green 0% // red 100%
buffer32[i] = 0x0000ff00 // blue 0% // red 100%
どうしたの?
編集: のMDN 記事にUint32Array
基づいて、 と不気味な突然変異を省いた後の同様の (悪い) 結果:ImageData.data
/**
* fails in exactly the same way
*/
function drawNoise( ctx, x, y, width, height ) {
let imageData = ctx.createImageData(width, height)
for (let i = 0, len = imageData.data.length; i < len; i += 4) {
imageData.data[i + 0] = 0
imageData.data[i + 1] = 0
imageData.data[i + 2] = 0
imageData.data[i + 3] = Math.random() < 0.5 ? 255 : 0
}
ctx.putImageData(imageData, x, y)
}