GSAP備忘録(その3)

目次
  1. CanvasとGSAP
  2. SVGとGSAP
  3. CSS(Filterと3DTransform)
  4. EndArrayとSnapPlugin

CanvasとGSAP

画像を複数行に分割して
複数のCanvas要素で表示する
drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)

sx      (元画像の切り抜き始点X)
sy      (元画像の切り抜き始点Y)
sWidth  (元画像の切り抜きサイズ:横幅)
sHeight (元画像の切り抜きサイズ:高さ)
dx      (Canvasの描画開始位置X)
dy      (Canvasの描画開始位置Y)
dWidth  (Canvasの描画サイズ:横幅)
dHeight (Canvasの描画サイズ:高さ)
<div id="parent" style="width:300px; height:200px"></div>
<style>
.canvas{
 display: block;
}
</style>
<script>
const parent = document.getElementById('parent')
const imgW = 640 //元画像
const imgH = 426
const canvasW = 300 //表示幅
const canvasH = imgH/imgW * canvasW
const low = 10 //行数
function loadCanvas(id) {
    let canvas = document.createElement('canvas');
    canvas.id = id;
    canvas.width = canvasW;
    canvas.height = canvasH/low;
    canvas.className = "block";
    parent.appendChild(canvas)
}
//それぞれのCanvasに画像を表示する
function loadImg(id,posY) {
    let canvas = document.getElementById(id);
    let c = canvas.getContext('2d');
    let imageObj = new Image();
    imageObj.onload = function() {
        let sourceX = 0;
        let sourceY = posY;
        let sourceWidth = imgW;
        let sourceHeight = imgH/low;
        let destWidth = canvasW
        let destHeight = canvasH/low
        let destX = 0;
        let destY = 0;
        c.drawImage(imageObj, sourceX, sourceY, sourceWidth, sourceHeight, destX, destY, destWidth, destHeight);
    }
   imageObj.src = '...';
}
for (let i = 0; i < low; i++) {
    let curCanvasId="canvas_Column" + i;
      loadCanvas(curCanvasId);
      loadImg(curCanvasId, i * imgH/low);
  }
</script>

それぞれのCanvasをGSAPを使ってアニメーションする
*画像をクリックした時にアニメーション

<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.10.4/gsap.min.js"></script>
<script>
//省略
const rotation = gsap.to(".canvas", { 
   duration: 3, 
   rotationY: 360,
   stagger: 0.1
  });
rotation.pause();
parent.addEventListener("click", ()=>{
  rotation.restart();
})
</script>

画像の上をクリック

staggerについて

stagger: 0.1
各トゥイーンの開始時間の間に 0.1 秒が生じます
負の値は最後の要素が最初に始まるようになります

staggerは詳細オプションの設定ができます
stagger:{ ここにオプション}

  • amount:合計時間 (秒単位)
  • each:各トゥイーンの間に一定時間
  • from:特定の要素から始める
    *配列の場合はインデックスを表す数値
    *”start”・”center”・”edges”・ “random”・ “end”
  • grid:要素が視覚的にグリッドに表示されている場合
  • axis:1つの軸だけに焦点を当てる
  • ease

画像は行列で分割
stagger詳細オプションでgridを試してみる

<style>
#parent{
display:flex;
flex-wrap: wrap;
}
</style>
<div id="parent" style="width:300px; height:200px"></div>
<script>
const parent = document.getElementById('parent')
const imgW = 640  //元画像
const imgH = 427
const canvasW = 300 //表示幅
const canvasH = imgH/imgW*canvasW
const low = 5 //行数
const col = 6  //列数
function loadCanvas(id) {
    let canvas = document.createElement('canvas');
    canvas.id = id;
    canvas.width = canvasW/col;
    canvas.height = canvasH/low;
    canvas.className = "canvas";
    parent.appendChild(canvas)
}
//それぞれのCanvasに画像を表示する
function loadImg(id, posY, posX) {
    let canvas = document.getElementById(id);
    let c = canvas.getContext('2d');
    let imageObj = new Image();
    imageObj.onload = function() {
        let sourceX = posX;
        let sourceY = posY;
        let sourceWidth = imgW/col
        let sourceHeight = imgH/low
        let destWidth = canvasW/col
        let destHeight = canvasH/low
        let destX = 0;
        let destY = 0;
        c.drawImage(imageObj, sourceX, sourceY, sourceWidth, sourceHeight, destX, destY, destWidth, destHeight);
    }
   imageObj.src = '...';
}
for (let j = 0; j < low; j++) {
  for (let i = 0; i < col; i++) {
     let id=`row${j}_col${i}`
     loadCanvas(id);
     loadImg(id, j * imgH/low, i * imgW/col);
    }
  }
}
//gsap
const tl = gsap.timeline();
const moveGrid = tl.to(".canvas", {
      duration: 1, x:300,
      stagger: { each: 0.1, grid: "auto", from: "edges"}
    })
  .to(".canvas", {
      duration: 1, x:0,
      stagger: { each: 0.1, grid: "auto", from: "start"}
    })
  .to(".canvas", {
      duration: 1, y:-200,
      stagger: { each: 0.1, grid: "auto", from: "random"}
    })
  .to(".canvas", {
      duration: 1, y: 0,
      stagger: { each: 0.1, grid: "auto", from: "end"}
    })
  .to(".canvas", {
      duration: 1, rotationZ:360,
      stagger: { each: 0.1, grid: "auto", from: "center"}
    })
moveGrid.pause();
parent.addEventListener("click", ()=> moveGrid.restart())
</script>

画像の上をクリック

SVGとGSAP

SVGオブジェクト(三角形)をgsap.setメソッドで描画
gsap.toメソッドでアニメーション

<div id="parent2" style="width:300px; height:200px; background:black;">
<svg id="svg" height=100% width=100%></svg>
</div>
<script>
class Triangle {
 constructor(id, x1, y1, x2, y2, x3, y3, fill){
  this.id = id;
  this.x1 = x1;
  this.y1 = y1;
  this.x2 = x2;
  this.y2 = y2;
  this.x3 = x3;
  this.y3 = y3;
  this.fill = fill;
 } 
 draw() {                       
    svg.innerHTML += 
     `<polygon id="${this.id}" class="triangle" points="${this.x1} ${this.y1}, ${this.x2} ${this.y2} ,${this.x3} ${this.y3}, ${this.x1} ${this.y1}" fill="${this.fill}"/>`;  
  }
}
function setTriangles() { 
  const w = 300
  const h = 200
  const row = 6
  const col = 10
  const gap = 5
  let stepX = Math.floor(w/col)
  let stepY = Math.floor(h/row)
  let y1 = gap, y2 =stepY-gap , y3 = stepY-gap
  let i = 0;
  for (let j = 0; j < row ; j++) {
    let x1 = gap, x2 = gap, x3 = stepX-gap;
    for (let k = 0; k <= col; k++) { 
      i++;
      new Triangle("triangle"+i, x1, y1, x2, y2, x3, y3, "yellow").draw();
      gsap.set("#triangle"+i, {
        transformOrigin:"-50% -50%", 
      }); 
      x1 +=stepX;
      x2 +=stepX;
      x3 +=stepX;
    }
    y1 += stepY;
    y2 += stepY;
    y3 += stepY;
  }
}
setTriangles()
const rotation2 = gsap.to(".triangle", {
  duration: 2, 
  rotation: 360,
  skewY: 180,
  stagger: function(index, target, list) { // インデックスの奇数と偶数で時間をずらす
   let gap = 0.5
   if(index % 2 == 0){ gap = 0.5}
    else { gap = 0.1}
   return index/index*gap
  }
  });
rotation2.pause();
const parent2 = document.getElementById('parent2')
parent2.addEventListener("click", ()=> {
rotation2.restart();
});
</script>

staggerに関数を設定できます

stagger : function ( index , target , list ) {
// カスタム ロジック
return //合計遅延を返す
}

画像の上をクリック


SVGフィルターをの属性を「AttrPlugin」でアニメーション
feTurbulence(ノイズを設定する)
feDisplacementMap(元の要素を変形し効果を適応する)

<div style="width:300px; height="200px;">
<svg id="container" viewBox="10 10 290 183">
  <defs>
    <filter id="displacementFilter">
      <feTurbulence id="feTurbulence" type="turbulence" baseFrequency="0.02" numOctaves="2" result="turbulence"></feTurbulence>
      <feDisplacementMap in2="turbulence" in="SourceGraphic" scale="10" xChannelSelector="B" yChannelSelector="R"></feDisplacementMap>
    </filter>
  </defs>
  <image xlink:href="..." width="300" height="200" style="filter:url(#displacementFilter)"></image>
</svg>
</div>
<script>
const svgFilter = gsap.to("#feTurbulence", 10, {attr:{baseFrequency:0.06}})
const container =document.getElementById('container')
svgFilter.pause();
container.addEventListener("click", ()=> {
svgFilter.restart();
});
</script>

画像の上をクリック

CSS(Filterと3DTransform)

filter:”hue-rotate(90deg)”で色を反転させます

<img id="filterImg" src="..." style="width:300px; height:200px;">
<script>
const timeline2 = gsap.timeline();
const tlFilter = timeline2
  .to("#filterImg", 5, {filter: 
    "hue-rotate(90deg)"})
  .to("#filterImg", 1,{filter: 
    "hue-rotate(0deg)"})
tlFilter.pause();
const filterImg =document.getElementById('filterImg')
filterImg.addEventListener("click", ()=> {
  tlFilter.restart();
});
</script>

画像の上をクリック

3DTransformで平面に奥行きをつけて変形させます

<div id="div" style="width:300px;">
  <img src="..." style="width:300px; height:200px;">
</div>
<script>
gsap.set("#div", {
transformOrigin: "50% 50% 200%", //x・y・zのoffsetの設定
transformPerspective: 2000,  //奥行き
rotationY: 0})
gsap.to("#div", 5, {rotationY: 360});
</script>

画像の上をクリック

設定なし

x・y・zのoffsetの設定あり
transformOrigin: “50% 50% 200%”

  • transformOriginは「 “top”・”left”・ “right”・ “bottom”キーワードの設定もできます」
  • 回転角度は時計回り (「_cw」)・反時計回り (「_ccw」)・最短方向 (「_short」) が使えます
    *例:rotation: “-170_short”, rotationX: “-=30_cw”

EndArrayとSnapPlugin

endArray
イージングを適用して数値の配列を別の数値の配列にトゥイーンします
*長さが異なる配列は両方の配列にあるインデックスのみがアニメーション化されます

<div id="rect1" style="width:20px;height:20px; background:red; margin:20px 0"></div>
<div id="rect2" style="width:20px;height:20px; background:blue; margin:20px 0"></div>
<div id="rect3" style="width:20px;height:20px; background:green; margin:20px 0"></div>
<div id="rect4" style="width:20px;height:20px; background:yellow; margin:20px 0"></div>
<script>
let array = [10, 20, 30, 280];
gsap.to(array, 5, {
    endArray: [100, 200, 280, 0],
    onUpdate() {
        gsap.set("#rect1", {x: array[0]});
        gsap.set("#rect2", {x: array[1]});
        gsap.set("#rect3", {x: array[2]});
        gsap.set("#rect4", {x: array[3]});
    },
    ease: "power2.out"
});
</script>

SnapPlugin
特定の配列またはインクリメント内の最も近い値に「スナップ」してトゥイーンします

<svg width="300" height="100"> 
    <circle id="cr1" cx="25" cy="50" r="25" fill="orange"/>
</svg>
<script>
gsap.to("#cr1", 5, { x: 250, ease: "none", snap: {x: 50}}); //インクリメント50
</script>