<div class="dummy-box-1"></div>
<div class="img-box">
<img class="target-img" src="https://raw.githubusercontent.com/KentaMiyazaki09/three_test/main/src/assets/images/photo/01.jpg" alt="">
<canvas class="target-canvas"></canvas>
</div>
<div class="dummy-box-2"></div>
.dummy-box-1,
.dummy-box-2 {
height: 300px;
background-color: orange;
}
.img-box {
position: relative;
max-width: 400px;
width: 100%;
margin: 20px auto;
line-height: 1;
font-size: 0;
}
.img-box.is-finished .target-img {
opacity: 1;
}
.img-box.is-finished .target-canvas {
opacity: 0;
}
.target-img {
width: 100%;
opacity: 0;
}
.target-canvas {
position: absolute;
top: 0;
left: 0;
imageRendering: 'pixelated'
}
// canvasを管理するclass
class PixelatedImg {
constructor(targetImg, imageEl, canvasEl, blockSize) {
this.targetImg = targetImg
this.imageEl = imageEl
this.canvasEl = canvasEl
this.blockSize = blockSize
}
// canvasを生成
create() {
const drawSize = {
width: this.imageEl.width / this.blockSize,
height: this.imageEl.height / this.blockSize,
}
Object.assign(this.canvasEl, drawSize)
this.canvasEl.getContext('2d').drawImage(this.imageEl, 0, 0, Object.values(drawSize))
const canvasSize = this.targetImg
Object.assign(this.canvasEl.style, {
width: `${canvasSize.width}px`,
height: `${canvasSize.height}px`,
})
}
update(blockSize) {
this.blockSize = blockSize
this.create()
}
}
// 画像の読み込み
async function loadImg(src) {
const Img = new Image()
Img.src = src
await Img.decode()
return Img
}
// canvasの生成
let myPixelatedImg = null
async function createCanvas() {
const targetImg = document.querySelector('.target-img')
const canvasEl = targetImg.nextElementSibling
// 画像の読み込みを待つ
const imageEl = await loadImg(targetImg.getAttribute('src'))
myPixelatedImg = new PixelatedImg(targetImg, imageEl, canvasEl, 30)
myPixelatedImg.create()
}
document.addEventListener('DOMContentLoaded', () => {
gsap.registerPlugin(ScrollTrigger)
createCanvas()
// gsapスクロールのアニメーション設定
const trigger = document.querySelector('.img-box')
ScrollTrigger.create({
trigger,
start: 'top-=50 center',
toggleClass: { targets: '.img-box', className: 'is-animation' },
onEnter: () => {
let blockSizeNum = myPixelatedImg.blockSize
function galleryTick() {
blockSizeNum -= 0.5
myPixelatedImg.update(blockSizeNum)
myReq = requestAnimationFrame(galleryTick)
if (blockSizeNum <= 1) {
// canvasのアニメーションが終わったら画像を表示
trigger.classList.add('is-finished')
cancelAnimationFrame(myReq)
}
}
galleryTick()
},
once: true,
})
// リサイズ
window.onresize = () => {
const blockSizeNum = myPixelatedImg.blockSize
myPixelatedImg.update(blockSizeNum)
}
})
This Pen doesn't use any external CSS resources.