<script type="text/js-worker">
const getNewPixel = valid =>
Array.from(new Array(4), (_1, index) => undefined).map((_2, pIndex, m) => {
const value = valid.reduce((acc, v) => acc + v[pIndex], 0)
return ~~(value / valid.length)
})
const redraw = d => {
self.postMessage({ type: "redrawing", isRedrawing: true })
const ctx = canvas.getContext("2d")
let currentProgress = 50
ctx.clearRect(0, 0, canvas.width, canvas.height)
d.forEach((r, yi, selfY) =>
r.forEach((p, xi, selfX) => {
ctx.fillStyle = `rgba(${p[0]}, ${p[1]}, ${p[2]}, ${p[3]})`
ctx.fillRect(xi, yi, 1, 1)
const progress = ~~(
50 +
((yi * selfX.length + xi) / (selfY.length * selfX.length)) * 50
)
if (currentProgress !== progress) {
currentProgress = progress
self.postMessage({ type: "progress", progress })
}
})
)
self.postMessage({ type: "redrawing", isRedrawing: false })
}
drawPixel = (p, x, y) => new Promise(res => {
const ctx = canvas.getContext("2d")
ctx.fillStyle = `rgba(${p[0]}, ${p[1]}, ${p[2]}, ${p[3]})`
ctx.fillRect(x, y, 1, 1)
res()
})
const blur = async data => {
let currentProgress = 0
let yi = 0, xi = 0, temp=[]
for (let line of data) {
xi = 0
for (let pixel of line) {
const top2 = data[yi - 1]
const top1 = data[yi - 1]
const mid = line
const bot1 = data[yi + 1]
const bot2 = data[yi + 1]
const round = [
...(top2 !== undefined
? [top2[xi - 2], top2[xi - 1], top2[xi], top2[xi + 1], top2[xi + 2]]
: []),
...(top1 !== undefined
? [top1[xi - 2], top1[xi - 1], top1[xi], top1[xi + 1], top1[xi + 2]]
: []),
...(mid !== undefined
? [mid[xi - 2], mid[xi - 1], mid[xi], mid[xi + 1], mid[xi + 2]]
: []),
...(bot1 !== undefined
? [bot1[xi - 2], bot1[xi - 1], bot1[xi], bot1[xi + 1], bot1[xi + 2]]
: []),
...(bot2 !== undefined
? [bot2[xi - 2], bot2[xi - 1], bot2[xi], bot2[xi + 1], bot2[xi + 2]]
: [])
]
const valid = round.filter(v => v)
const np = valid.length > 0 ? getNewPixel(valid) : line[xi]
await drawPixel(np, xi, yi)
temp.push(np)
const progress = ~~(
((yi * line.length + xi) / (line.length * data.length)) * 100
)
if (currentProgress !== progress) {
currentProgress = progress
self.postMessage({ type: "progress", progress })
await new Promise(res => setTimeout(() => res(), 0))
}
xi++
}
pixels[yi] = temp
temp = []
yi++
}
}
const changeBrightness = async (data, value = 0) => {
let currentProgress = 0
let yi = 0, xi = 0, temp=[]
for (let line of data) {
xi = 0
for (let pixel of line) {
const np = [pixel[0] + value, pixel[1] + value, pixel[2] + value, pixel[3]]
const progress = ~~(
((yi * line.length + xi) / (line.length * data.length)) * 100
)
await drawPixel(np, xi, yi)
if (currentProgress !== progress) {
currentProgress = progress
self.postMessage({ type: "progress", progress })
await new Promise(res => setTimeout(() => res(), 0))
}
pixels[yi][xi] = np
xi++
}
yi++
}
}
const getImageData = async () => {
canvas.width = image.width
canvas.height = image.height
const ctx = canvas.getContext("2d")
ctx.drawImage(image, 0, 0)
return new Promise(async res => {
setTimeout(() => {
let currentProgress = 0
imageData = ctx.getImageData(0, 0, image.width, image.height)
res(
Array.from(new Array(imageData.height), (_, y) =>
Array.from(new Array(imageData.width), (_, x) => {
const s = y * imageData.width * 4 + x * 4
const progress = ~~(
((y * imageData.width + x) /
(imageData.height * imageData.width)) *
100
)
if (currentProgress !== progress) {
currentProgress = progress
self.postMessage({ type: "progress", progress })
}
return imageData.data.slice(s, s + 4)
})
)
)
})
})
}
let canvas, image, imageData, pixels, pixelRatio
self.onmessage = async ({ data }) => {
try {
const { type } = data
if (type === "canvas") {
canvas = data.canvas
const ctx = canvas.getContext("2d")
ctx.font = "30px Arial";
ctx.fillText("Hello From Worker", 20, 50);
pixelRatio = data.pixelRatio
} else if (type === "image") {
image = data.image
pixels = await getImageData()
self.postMessage({ type: "onEnd" })
} else if (type === "brightness") {
await changeBrightness(pixels, parseInt(data.value, 10))
self.postMessage({ type: "onEnd" })
} else if (type === "blur") {
await blur(pixels)
self.postMessage({ type: "onEnd" })
}
} catch (error) {
console.error(error)
self.postMessage("Error:", error.message)
}
}
</script>
<input onchange="handleImageUpload(this.files)" type="file" />
<div id="setting" class="setting">0</div>
<input type="range" min="-255" max="255" value="0" onchange="handleBrightnessChange(this.value)">
<button onClick="handleBrightness()">Change Brightness</button>
<button onClick="handleBlur()">Blur</button>
<div class="progressContainer">
<div>Progress:</div>
<div class="barContainer">
<div class="progressBar" id="progressBar" style="transform: scaleX(0);"> </div>
</div>
</div>
<canvas id="imageCanvas"></canvas>
<div class="animations">
<div class="transformAnimation"></div>
<div class="marginAnimation"></div>
</div>
<!--
<button onclick="initWorkerLoad()">Get me some primes!</button>
<br />
Animated with CSS
<div class="animations">
<div class="transformAnimation"></div>
<div class="marginAnimation"></div>
</div>
Animated with JS
<div class="animations">
<div class="static" id="static" style={{ transform: `translate(${this.state.margin}px)` }}></div>
</div>
</div>
</div> -->
.progressContainer {
width: 600px;
padding: 20px;
margin: 0px;
.barContainer {
border-radius: 5px;
overflow: hidden;
border: 1px solid #aaa;
width: 100%;
height: 20px;
margin: 0;
.progressBar {
margin: 0;
width: 100%;
height: 100%;
background-color: #09f;
transform-origin: left;
transition: 0.1s;
}
}
}
.setting {
display: inline-block;
width: 40px;
}
.animations {
background-color: "red";
width: 300px;
height: 40px;
position: relative;
}
.transformAnimation,
.marginAnimation,
.static {
position: absolute;
left: 0;
width: 20px;
height: 20px;
border-radius: 5px;
margin: 0;
}
.transformAnimation {
top: 0px;
background-color: #55f;
animation: transformLeft 4s infinite linear;
}
.marginAnimation {
top: 20px;
background-color: #f55;
animation: marginLeft 2s infinite linear;
}
.static {
top: 30px;
background-color: #555;
}
@keyframes transformLeft {
0% {
transform: translate(0px);
}
100% {
transform: translate(300px);
}
}
@keyframes marginLeft {
from {
margin-left: 0px;
}
to {
margin-left: 300px;
}
}
@keyframes rotate {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
View Compiled
var blob = new Blob(
Array.prototype.map.call(
document.querySelectorAll('script[type="text\/js-worker"]'),
function (oScript) { return oScript.textContent; }
),
{ type: 'text/javascript' }
)
// Create a new worker containing all "text/js-worker" scripts
const worker = new Worker(window.URL.createObjectURL(blob))
// Listen for a message from the worker
worker.addEventListener('message', e => {
const { type, progress } = e.data
if (type === "progress") {
document.getElementById("progressBar").style.transform = `scaleX(${progress / 100})`
} else if (type === "onEnd") {
document.getElementById("progressBar").style.transform = 'scaleX(0)'
}
// appendToList(e.data[e.data.length - 1])
})
// hand off canvas control to worker
const offscreenControl = document.getElementById('imageCanvas').transferControlToOffscreen()
worker.postMessage(
{
type: "canvas",
canvas: offscreenControl,
pixelRatio: window.devicePixelRatio
},
[offscreenControl]
)
appendToList = (content) => {
// Create a <li> node
let node = document.createElement("LI")
// Append the text to <li>
node.appendChild(document.createTextNode(content))
document.getElementById("primes").appendChild(node);
}
// send image to worker
onImageLoad = async () => {
const image = await createImageBitmap(img)
console.log('posting image', image)
worker.postMessage({ type: "image", image }, [image])
}
onImageError = e => {
console.error(e)
}
// create img file
let img = new Image()
img.onload = onImageLoad
img.onerror = onImageError
handleImageUpload = (files) => {
img.src = URL.createObjectURL(files[0])
}
initWorkerLoad = () => {
worker.postMessage({ start: true })
}
// send commands to worker
handleBrightness = () => worker.postMessage({ type: "brightness", value: brightnessSetting })
handleBlur = () => worker.postMessage({ type: "blur" })
let brightnessSetting = 0
handleBrightnessChange = (val) => {
console.log(val)
brightnessSetting = val
document.getElementById("setting").innerHTML = brightnessSetting
}
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.