Selaa lähdekoodia

Declarative animation syntax

Alan Colon 7 vuotta sitten
vanhempi
sitoutus
324c1b26af
7 muutettua tiedostoa jossa 196 lisäystä ja 5 poistoa
  1. 3 0
      package.json
  2. 4 0
      src/animations.js
  3. 29 2
      src/index.css
  4. 2 1
      src/index.html
  5. 106 2
      src/index.js
  6. 47 0
      src/util.js
  7. 5 0
      yarn.lock

+ 3 - 0
package.json

@@ -32,5 +32,8 @@
     "webpack-cli": "^3.1.2",
     "webpack-dev-server": "^3.1.14",
     "webpack-livereload-plugin": "^2.2.0"
+  },
+  "dependencies": {
+    "animejs": "^3.0.1"
   }
 }

+ 4 - 0
src/animations.js

@@ -0,0 +1,4 @@
+const slideUp = { translateY: -100 }
+module.exports = {
+  slideUp
+}

+ 29 - 2
src/index.css

@@ -1,7 +1,34 @@
 @import url('https://fonts.googleapis.com/css?family=Roboto');
 
+html {
+  outline: solid 2px blue;
+  overflow: hidden;
+}
 body {
-  background-color: #333338;
-  color: white;
+  margin: 0;
+  outline: solid 2px red;
+  background-color: #252423;
+  color: #F6F4F2;
   font-family: Roboto;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  position: absolute;
+  width: 100vw;
+  height: 100vh;
+}
+
+sprite, .sprite {
+  display: inline-block;
+}
+centerer {
+  display: inline-block;
+  transform: translate(-50%, -50%);
+}
+mover, scaler {
+  display: inline-block;
+}
+mover {
+  display: inline-block;
+  position: absolute;
 }

+ 2 - 1
src/index.html

@@ -3,6 +3,7 @@
     <title>CHANGEME</title>
   </head>
   <body>
-    <h1>CHANGEME</h1>
+      <sprite id="frown" left="50" top="50" translateX[0]="-50" translateX[1.5]="0" translateX[3]="-50">🙁</sprite>
+      <sprite id="smile" left="0" top="0" scale[0,1,2,3]="1,2,3,4" translateX[0,1,2,3,4]="0,100vw,100vw,0,50vw" translateY[0,1,2,3,4]="0,0,100vh,100vh,50vh" duration[4]="6" rotate[4]="720" easing[4]="easeOutSine">😊</sprite>
   </body>
 </html>

+ 106 - 2
src/index.js

@@ -1,4 +1,108 @@
 require('./index.css')
+const anime = require('animejs').default
+const animations = require('./animations')
+const { getAttrs, unit, transforms } = require('./util')
 document.addEventListener('DOMContentLoaded', () => {
-  console.log(roboto)
-})
+  const sprites = Array.from(document.querySelectorAll('sprite, .sprite'))
+
+  const spritesById = {}
+  sprites
+    .filter(sprite => sprite.id)
+    .forEach(sprite => spritesById[sprite.id] = sprite)
+  const timeline = anime.timeline()
+  const initialize = sprite => {
+    const mover = document.createElement('mover')
+    const centerer = document.createElement('centerer')
+    const scaler = document.createElement('scaler')
+    mover.appendChild(centerer)
+    centerer.appendChild(scaler)
+    sprite.replaceWith(mover)
+    scaler.appendChild(sprite)
+    if (sprite.initialized) return
+    sprite.initialized = true
+
+    sprite.keyFrames = {}
+    sprite.moverFrames = {}
+    ;['left', 'right', 'top', 'bottom', 'width', 'height', 'opacity'].forEach(key => {
+      const val = sprite.attributes[key]
+      if (val) mover.style[key] = unit(val)
+    })
+
+    const getFrame = (time) => {
+      const key = time.toString()
+      if (!sprite.keyFrames[key]) {
+        sprite.keyFrames[key] = {targets: scaler}
+      }
+      return sprite.keyFrames[key]
+    }
+    const getMoverFrame = (time) => {
+      const key = time.toString()
+      if (!sprite.moverFrames[key]) {
+        sprite.moverFrames[key] = {targets: mover}
+      }
+      return sprite.moverFrames[key]
+    }
+
+    Array.from(sprite.attributes).forEach(attr => {
+      let {1:name, 2:time} = (/^([^\]]*)\[([^\]]*)\]$/.exec(attr.name) || [null, attr.name, "0"])
+      const transform = transforms[name]
+      if (transform) {
+        const times = time.split(',').map(x => +x)
+        const values = attr.value.split(',').map(x => x.trim())
+        if (times.length !== values.length) return console.error('Mismatched time, value arrays', attr)
+        times.forEach((time, i) => {
+          const value = values[i]
+          console.log(`${transform.name} ${value} at ${time}`, attr)
+          if (transform.global) {
+            getMoverFrame(time)[transform.name] = value
+            getFrame(time)[transform.name] = value
+          } else if (transform.mover) {
+            getMoverFrame(time)[transform.name] = value
+          } else {
+            getFrame(time)[transform.name] = value
+          }
+        })
+      }
+      sprite.removeAttribute(attr.name)
+    })
+    // if (anim) {
+    //   anime(Object.assign({
+    //     targets: sprite
+    //   }, animations[anim]))
+    // }
+  }
+
+  sprites.forEach(initialize)
+
+  const allFrames = []
+  sprites.forEach(({keyFrames, moverFrames}) => {
+    const frames = [...Object.entries(keyFrames), ...Object.entries(moverFrames)].map(([time, frame]) => [+time * 1000, frame])
+    frames.sort((a, b) => a[0] - b[0])
+    frames.forEach(frame => {
+      frame[1].targets.lastTime = 0
+      if (frame[1].duration) {
+        frame[1].duration = 1000 * +frame[1].duration
+      }
+    })
+    frames.forEach(frame => {
+      allFrames.push(frame)
+      if (frame[0] == 0) {
+        frame[0] = Number.EPSILON
+        frame[1].easing = 'steps(1)'
+      } else {
+        frame[1].duration = frame[1].duration || Math.min(1000, frame[0] - frame[1].targets.lastTime)
+      }
+      frame[1].targets.lastTime = frame[0]
+    })
+  })
+  allFrames.sort((a, b) => a[0] - b[0])
+
+  console.log(allFrames)
+  allFrames.forEach(([time, keyFrame]) => {
+    timeline.add(keyFrame, time)
+    console.log(time, keyFrame)
+  })
+
+  console.log(timeline)
+})
+

+ 47 - 0
src/util.js

@@ -0,0 +1,47 @@
+const getAttrs = (element) => {
+  const attrs = {}
+  Array.from(element.attributes).forEach(attr => {
+    attrs[attr.name] = +attr.value || attr.value
+  })
+}
+
+const unit = (value, u = '%') => {
+  if (value === undefined || value === null) return null
+  if (value instanceof Attr) value = value.value
+  if (typeof value === 'number') value = value.toString()
+  if (typeof value === 'string' && /^[\d\.]*$/.test(value)) value += u
+  return value
+}
+
+const vh = value => unit(value, 'vh')
+const vw = value => unit(value, 'vw')
+
+const transforms = [
+  {name: 'translateX', unit: 'px', mover: true },
+  {name: 'translateY', unit: 'px', mover: true },
+  {name: 'translateZ', unit: 'px', mover: true },
+  {name: 'rotate', unit: 'deg' },
+  {name: 'rotateX', unit: 'deg' },
+  {name: 'rotateY', unit: 'deg' },
+  {name: 'rotateZ', unit: 'deg' },
+  {name: 'scale', unit: '' },
+  {name: 'scaleX', unit: '' },
+  {name: 'scaleY', unit: '' },
+  {name: 'scaleZ', unit: '' },
+  {name: 'skew', unit: 'deg' },
+  {name: 'skewX', unit: 'deg' },
+  {name: 'skewY', unit: 'deg' },
+  {name: 'perspective', unit: 'px' },
+  {name: 'duration', unit: '', global: true},
+  {name: 'easing', unit: '', global: true}
+]
+
+transforms.forEach(t => transforms[t.name.toLowerCase()] = t)
+
+module.exports = {
+  getAttrs,
+  unit,
+  vh,
+  vw,
+  transforms
+}

+ 5 - 0
yarn.lock

@@ -200,6 +200,11 @@ ajv@^6.1.0, ajv@^6.5.5:
     json-schema-traverse "^0.4.1"
     uri-js "^4.2.2"
 
+animejs@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/animejs/-/animejs-3.0.1.tgz#8b808bad575e3b6106489d8b04b92bc4062166fe"
+  integrity sha512-7mMoBQScvA7s4zm3EVgnuXXrtOu6TJNAhU+Ajo7HsXYH0NwbuZIcRHMO65a9NyUlmAXz4VPxlK5cENNOQbCi8g==
+
 ansi-colors@^3.0.0:
   version "3.2.3"
   resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.3.tgz#57d35b8686e851e2cc04c403f1c00203976a1813"