Skip to content

Commit

Permalink
feat: replace draw rect by draw image to improve performance [#1]
Browse files Browse the repository at this point in the history
  • Loading branch information
d3p1 committed Sep 26, 2024
1 parent fce4d74 commit d1230fc
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 55 deletions.
2 changes: 1 addition & 1 deletion docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<script type="module">
import Img2Pxl from './js/img2pxl.js'

new Img2Pxl('./images/logo.png', 0.3, 0.6, 40).animate()
new Img2Pxl('./images/logo.png', 70, 70, 0.3, 0.6, 40).animate()
</script>
</body>
</html>
64 changes: 33 additions & 31 deletions docs/js/core/pixel.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,70 +16,72 @@ export default class Pixel {
/**
* @type {number}
*/
red
width

/**
* @type {number}
*/
green
height

/**
* @type {number}
* @param {HTMLImageElement}
*/
blue
#img

/**
* @type {number}
*/
#force
#imgX

/**
* @type {number}
*/
#originX
#imgY

/**
* @type {number}
*/
#originY
#force

/**
* Constructor
*
* @param {number} x
* @param {number} y
* @param {number} red
* @param {number} green
* @param {number} blue
* @param {number} force
* @param {HTMLImageElement} img
* @param {number} x
* @param {number} y
* @param {number} width
* @param {number} height
* @param {number} force
*/
constructor(x, y, red, green, blue, force) {
constructor(img, x, y, width, height, force) {
this.x = x
this.y = y
this.red = red
this.green = green
this.blue = blue
this.width = width
this.height = height
this.#img = img
this.#imgX = x
this.#imgY = y
this.#force = force
this.#originX = x
this.#originY = y
}

/**
* Draw particle
*
* @param {CanvasRenderingContext2D} ctx
* @returns {void}
* @note It is used a rect instead of a circle as pixel shape for
* performance reasons
* @note The save and restore of drawing state is delegate to caller logic
* for performance reasons.
* It is very important to restore drawing
* state after this method execution, to avoid pixel styles being
* applied to other rendered shapes
*/
draw(ctx) {
ctx.fillStyle = `rgb(${this.red}, ${this.green}, ${this.blue})`
ctx.fillRect(this.x, this.y, 1, 1)
ctx.drawImage(
this.#img,
this.#imgX,
this.#imgY,
this.width,
this.height,
this.x,
this.y,
this.width,
this.height,
)
}

/**
Expand All @@ -90,12 +92,12 @@ export default class Pixel {
* @todo Use integer values for pixel coordinates to avoid antti-aliasing
*/
update(t) {
if (this.x === this.#originX && this.y === this.#originY) {
if (this.x === this.#imgX && this.y === this.#imgY) {
return
}

const xDistance = this.x - this.#originX
const yDistance = this.y - this.#originY
const xDistance = this.x - this.#imgX
const yDistance = this.y - this.#imgY
this.x -= xDistance * this.#force * (t / 250)
this.y -= yDistance * this.#force * (t / 250)
}
Expand Down
57 changes: 34 additions & 23 deletions docs/js/img2pxl.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,22 @@ export default class Img2Pxl {
* Constructor
*
* @param {string} imageSrc
* @param {number} resolutionWidth
* @param {number} resolutionHeight
* @param {number} pixelForce
* @param {number} pointerForce
* @param {number} pointerRadius
*/
constructor(imageSrc, pixelForce, pointerForce, pointerRadius) {
constructor(
imageSrc,
resolutionWidth,
resolutionHeight,
pixelForce,
pointerForce,
pointerRadius,
) {
this.#initCanvas()
this.#initImage(imageSrc)
this.#initImage(imageSrc, resolutionWidth, resolutionHeight)
this.#initPointer()
this.#pixelForce = pixelForce
this.#pointerForce = pointerForce
Expand Down Expand Up @@ -131,42 +140,44 @@ export default class Img2Pxl {
* Init image
*
* @param {string} src
* @param {number} resolutionWidth
* @param {number} resolutionHeight
* @returns {void}
*/
#initImage(src) {
#initImage(src, resolutionWidth, resolutionHeight) {
const img = new Image()
img.src = src
img.onload = () => {
this.#canvas.width = img.width
this.#canvas.height = img.height
this.#ctx.drawImage(img, 0, 0)

this.#initPixels()
this.#initPixels(img, resolutionWidth, resolutionHeight)
}
}

/**
* Init image pixels
*
* @param {HTMLImageElement} img
* @param {number} resolutionWidth
* @param {number} resolutionHeight
* @returns {void}
*/
#initPixels() {
const imageData = this.#ctx.getImageData(
0,
0,
this.#canvas.width,
this.#canvas.height,
)

const data = imageData.data
for (let i = 0; i < data.length; i += 4) {
if (data[i + 3] > 0) {
const x = Math.floor((i % (imageData.width * 4)) / 4)
const y = Math.floor(i / (imageData.width * 4))
this.#pixels.push(
new Pixel(x, y, data[i], data[i + 1], data[i + 2], this.#pixelForce),
)
}
#initPixels(img, resolutionWidth, resolutionHeight) {
const pixelWidth = img.width / resolutionWidth
const pixelHeight = img.height / resolutionHeight

for (let i = 0; i < resolutionWidth * resolutionHeight; i++) {
const xDisplacement = pixelWidth * i
this.#pixels.push(
new Pixel(
img,
xDisplacement % img.width,
pixelHeight * (xDisplacement / img.width),
pixelWidth,
pixelHeight,
this.#pixelForce,
),
)
}
}
}

0 comments on commit d1230fc

Please sign in to comment.