├── .gitignore ├── static ├── icon.png ├── ground.jpg ├── icon.jpeg ├── favicon.ico ├── meatball-parade.mp3 └── README.md ├── plugins ├── motion.js ├── global-events.js ├── presets.js ├── README.md ├── three.js ├── ga.js └── tweezing.js ├── layouts ├── default.vue └── README.md ├── .eslintrc.js ├── components ├── README.md ├── three │ ├── AmbientLight.js │ ├── Scene.js │ ├── SpotLight.js │ ├── Cube.js │ ├── Ground.js │ ├── Camera.js │ ├── Renderer.js │ └── Object3D.js ├── AppLogo.vue └── music.vue ├── pages ├── music.vue ├── README.md ├── basic-transition.vue ├── sudoku.vue ├── easing-mouse.vue ├── index.vue ├── sudoku-motion.vue ├── plot-motion.vue ├── polygon.vue ├── three.vue ├── easings-graph.vue └── motion-graph.vue ├── .editorconfig ├── motions ├── package.json ├── index.js ├── yarn.lock └── App.vue ├── assets ├── README.md ├── datasets.json └── main.css ├── middleware └── README.md ├── store └── README.md ├── package.json ├── nuxt.config.js ├── README.md └── pizzi.html /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | .nuxt 4 | npm-debug.log 5 | sw.* 6 | .DS_Store 7 | -------------------------------------------------------------------------------- /static/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/posva/state-animation-demos/HEAD/static/icon.png -------------------------------------------------------------------------------- /static/ground.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/posva/state-animation-demos/HEAD/static/ground.jpg -------------------------------------------------------------------------------- /static/icon.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/posva/state-animation-demos/HEAD/static/icon.jpeg -------------------------------------------------------------------------------- /static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/posva/state-animation-demos/HEAD/static/favicon.ico -------------------------------------------------------------------------------- /plugins/motion.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Motion from 'vue-motion' 3 | 4 | Vue.use(Motion) 5 | -------------------------------------------------------------------------------- /layouts/default.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /static/meatball-parade.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/posva/state-animation-demos/HEAD/static/meatball-parade.mp3 -------------------------------------------------------------------------------- /plugins/global-events.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import GlobalEvents from 'vue-global-events' 3 | 4 | Vue.component('GlobalEvents', GlobalEvents) 5 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | browser: true, 5 | node: true, 6 | }, 7 | extends: ['posva'], 8 | // add your custom rules here 9 | rules: {}, 10 | } 11 | -------------------------------------------------------------------------------- /components/README.md: -------------------------------------------------------------------------------- 1 | # COMPONENTS 2 | 3 | The components directory contains your Vue.js Components. 4 | Nuxt.js doesn't supercharge these components. 5 | 6 | **This directory is not required, you can delete it if you don't want to use it.** 7 | -------------------------------------------------------------------------------- /components/three/AmbientLight.js: -------------------------------------------------------------------------------- 1 | import { AmbientLight } from 'three' 2 | import Object3D from './Object3D' 3 | 4 | export default { 5 | mixins: [Object3D], 6 | 7 | beforeMount () { 8 | this.obj = new AmbientLight('#eee') 9 | }, 10 | } 11 | -------------------------------------------------------------------------------- /pages/music.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 16 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_size = 2 6 | indent_style = space 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /motions/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "motions", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "license": "MIT", 6 | "dependencies": { 7 | "lodash": "^4.17.5", 8 | "modern-normalize": "^0.2.0", 9 | "vue": "^2.5.13", 10 | "vue-motion": "^0.2.0" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /layouts/README.md: -------------------------------------------------------------------------------- 1 | # LAYOUTS 2 | 3 | This directory contains your Application Layouts. 4 | 5 | More information about the usage of this directory in the documentation: 6 | https://nuxtjs.org/guide/views#layouts 7 | 8 | **This directory is not required, you can delete it if you don't want to use it.** 9 | -------------------------------------------------------------------------------- /pages/README.md: -------------------------------------------------------------------------------- 1 | # PAGES 2 | 3 | This directory contains your Application Views and Routes. 4 | The framework reads all the .vue files inside this directory and creates the router of your application. 5 | 6 | More information about the usage of this directory in the documentation: 7 | https://nuxtjs.org/guide/routing 8 | -------------------------------------------------------------------------------- /assets/README.md: -------------------------------------------------------------------------------- 1 | # ASSETS 2 | 3 | This directory contains your un-compiled assets such as LESS, SASS, or JavaScript. 4 | 5 | More information about the usage of this directory in the documentation: 6 | https://nuxtjs.org/guide/assets#webpacked 7 | 8 | **This directory is not required, you can delete it if you don't want to use it.** 9 | -------------------------------------------------------------------------------- /motions/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Motion from 'vue-motion' 3 | import 'modern-normalize/modern-normalize.css' 4 | import App from './App' 5 | 6 | Vue.use(Motion) 7 | 8 | Vue.config.devtools = true 9 | Vue.config.performance = true 10 | 11 | window.vm = new Vue({ 12 | el: '#app', 13 | render: h => h(App), 14 | }) 15 | -------------------------------------------------------------------------------- /plugins/presets.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | export default { 3 | noWobble: { stiffness: 170, damping: 26, precision: 0.01 }, // the default, if nothing provided 4 | gentle: { stiffness: 120, damping: 14, precision: 0.01 }, 5 | wobbly: { stiffness: 180, damping: 12, precision: 0.01 }, 6 | stiff: { stiffness: 210, damping: 20, precision: 0.01 }, 7 | } 8 | -------------------------------------------------------------------------------- /plugins/README.md: -------------------------------------------------------------------------------- 1 | # PLUGINS 2 | 3 | This directory contains your Javascript plugins that you want to run before instantiating the root vue.js application. 4 | 5 | More information about the usage of this directory in the documentation: 6 | https://nuxtjs.org/guide/plugins 7 | 8 | **This directory is not required, you can delete it if you don't want to use it.** 9 | -------------------------------------------------------------------------------- /static/README.md: -------------------------------------------------------------------------------- 1 | # STATIC 2 | 3 | This directory contains your static files. 4 | Each file inside this directory is mapped to /. 5 | 6 | Example: /static/robots.txt is mapped as /robots.txt. 7 | 8 | More information about the usage of this directory in the documentation: 9 | https://nuxtjs.org/guide/assets#static 10 | 11 | **This directory is not required, you can delete it if you don't want to use it.** 12 | -------------------------------------------------------------------------------- /middleware/README.md: -------------------------------------------------------------------------------- 1 | # MIDDLEWARE 2 | 3 | This directory contains your Application Middleware. 4 | The middleware lets you define custom function to be ran before rendering a page or a group of pages (layouts). 5 | 6 | More information about the usage of this directory in the documentation: 7 | https://nuxtjs.org/guide/routing#middleware 8 | 9 | **This directory is not required, you can delete it if you don't want to use it.** 10 | -------------------------------------------------------------------------------- /store/README.md: -------------------------------------------------------------------------------- 1 | # STORE 2 | 3 | This directory contains your Vuex Store files. 4 | Vuex Store option is implemented in the Nuxt.js framework. 5 | Creating a index.js file in this directory activate the option in the framework automatically. 6 | 7 | More information about the usage of this directory in the documentation: 8 | https://nuxtjs.org/guide/vuex-store 9 | 10 | **This directory is not required, you can delete it if you don't want to use it.** 11 | -------------------------------------------------------------------------------- /components/three/Scene.js: -------------------------------------------------------------------------------- 1 | import Object3D from './Object3D' 2 | import { Scene, Color } from 'three' 3 | 4 | export default { 5 | ...Object3D, 6 | inject: ['renderer'], 7 | 8 | provide () { 9 | this.scene = new Scene() 10 | return { 11 | scene: this.scene, 12 | } 13 | }, 14 | 15 | // overwrite existing mounted 16 | mounted () { 17 | this.renderer.scene = this.scene 18 | this.scene.background = new Color(0x111111) 19 | // console.log('scene', this.scene) 20 | }, 21 | } 22 | -------------------------------------------------------------------------------- /components/three/SpotLight.js: -------------------------------------------------------------------------------- 1 | import { SpotLight } from 'three' 2 | import Object3D from './Object3D' 3 | 4 | export default { 5 | mixins: [Object3D], 6 | 7 | beforeMount () { 8 | this.obj = new SpotLight(0x404040) 9 | this.obj.angle = Math.PI / 5 10 | this.obj.penumbra = 0.3 11 | this.obj.position.set(0, 10, 5) 12 | this.obj.castShadow = true 13 | this.obj.shadow.camera.near = 8 14 | this.obj.shadow.camera.far = 30 15 | this.obj.shadow.mapSize.width = 1024 16 | this.obj.shadow.mapSize.height = 1024 17 | }, 18 | } 19 | -------------------------------------------------------------------------------- /components/three/Cube.js: -------------------------------------------------------------------------------- 1 | import { BoxGeometry, MeshPhongMaterial, Mesh } from 'three' 2 | import Object3D from './Object3D' 3 | 4 | export default { 5 | extends: Object3D, 6 | 7 | props: ['color'], 8 | 9 | beforeMount () { 10 | this.geometry = new BoxGeometry(1, 1, 1) 11 | this.material = new MeshPhongMaterial({ 12 | color: this.color, 13 | shininess: 150, 14 | specular: 0x222222, 15 | }) 16 | this.obj = new Mesh(this.geometry, this.material) 17 | this.obj.castShadow = true 18 | this.obj.receiveShadow = true 19 | }, 20 | } 21 | -------------------------------------------------------------------------------- /components/three/Ground.js: -------------------------------------------------------------------------------- 1 | import { BoxGeometry, MeshPhongMaterial, Mesh, TextureLoader, RepeatWrapping } from 'three' 2 | import Object3D from './Object3D' 3 | 4 | export default { 5 | extends: Object3D, 6 | beforeMount () { 7 | this.geometry = new BoxGeometry(10, 0.15, 10) 8 | const texture = new TextureLoader().load('/ground.jpg') 9 | texture.wrapS = RepeatWrapping 10 | texture.wrapT = RepeatWrapping 11 | texture.repeat.set(6, 6) 12 | this.material = new MeshPhongMaterial({ 13 | map: texture, 14 | shininess: 150, 15 | }) 16 | this.obj = new Mesh(this.geometry, this.material) 17 | this.obj.scale.multiplyScalar(3) 18 | this.obj.castShadow = false 19 | this.obj.receiveShadow = true 20 | }, 21 | } 22 | -------------------------------------------------------------------------------- /motions/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | lodash@^4.17.5: 6 | version "4.17.5" 7 | resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.5.tgz#99a92d65c0272debe8c96b6057bc8fbfa3bed511" 8 | 9 | modern-normalize@^0.2.0: 10 | version "0.2.0" 11 | resolved "https://registry.yarnpkg.com/modern-normalize/-/modern-normalize-0.2.0.tgz#d587ceab41682ea17650e76891020c8388df258d" 12 | 13 | vue-motion@^0.2.0: 14 | version "0.2.0" 15 | resolved "https://registry.yarnpkg.com/vue-motion/-/vue-motion-0.2.0.tgz#5ce9441eea605624d66d0d94688d1f8ef26bfe6f" 16 | 17 | vue@^2.5.13: 18 | version "2.5.13" 19 | resolved "https://registry.yarnpkg.com/vue/-/vue-2.5.13.tgz#95bd31e20efcf7a7f39239c9aa6787ce8cf578e1" 20 | -------------------------------------------------------------------------------- /plugins/three.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Scene from '~/components/three/Scene' 3 | import Cube from '~/components/three/Cube' 4 | import Ground from '~/components/three/Ground' 5 | import Renderer from '~/components/three/Renderer' 6 | import Camera from '~/components/three/Camera' 7 | import AmbientLight from '~/components/three/AmbientLight' 8 | import SpotLight from '~/components/three/SpotLight' 9 | import * as THREE from 'three' 10 | // import VueThreejs from 'vue-threejs' 11 | // Vue.use(VueThreejs) 12 | 13 | Vue.component('Scene', Scene) 14 | Vue.component('Cube', Cube) 15 | Vue.component('Ground', Ground) 16 | Vue.component('Renderer', Renderer) 17 | Vue.component('Camera', Camera) 18 | Vue.component('AmbientLight', AmbientLight) 19 | Vue.component('SpotLight', SpotLight) 20 | -------------------------------------------------------------------------------- /components/three/Camera.js: -------------------------------------------------------------------------------- 1 | import Object3D from './Object3D' 2 | import { PerspectiveCamera } from 'three' 3 | // import { OrbitControls } from '~/plugins/three' 4 | 5 | export default { 6 | extends: Object3D, 7 | inject: ['renderer'], 8 | 9 | provide () { 10 | this.obj = new PerspectiveCamera(75, this.renderer.width / this.renderer.height, 0.1, 1000) 11 | // new OrbitControls(this.camera) 12 | return { 13 | camera: this.obj, 14 | } 15 | }, 16 | 17 | watch: { 18 | position: { 19 | handler () { 20 | if (!this.obj) return 21 | this.obj.lookAt(this.scene.position) 22 | }, 23 | deep: true, 24 | }, 25 | }, 26 | 27 | mounted () { 28 | this.renderer.camera = this.obj 29 | this.obj.lookAt(this.scene.position) 30 | // console.log('camera', this.obj) 31 | }, 32 | } 33 | -------------------------------------------------------------------------------- /pages/basic-transition.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 25 | 26 | 44 | -------------------------------------------------------------------------------- /plugins/ga.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | 3 | export default ({ app }) => { 4 | /* 5 | ** Only run on client-side and only in production mode 6 | */ 7 | if (process.env.NODE_ENV !== 'production') return 8 | /* 9 | ** Include Google Analytics Script 10 | */ 11 | (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ 12 | (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), 13 | m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) 14 | })(window,document,'script','https://www.google-analytics.com/analytics.js','ga'); 15 | /* 16 | ** Set the current page 17 | */ 18 | ga('create', 'UA-114346078-1', 'auto') 19 | /* 20 | ** Every time the route changes (fired on initialization too) 21 | */ 22 | app.router.afterEach((to, from) => { 23 | /* 24 | ** We tell Google Analytics to add a `pageview` 25 | */ 26 | ga('set', 'page', to.fullPath) 27 | ga('send', 'pageview') 28 | }) 29 | } 30 | -------------------------------------------------------------------------------- /plugins/tweezing.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import { Tweezing, tweenjsHelper } from 'vue-tweezing' 3 | import TWEEN from '@tweenjs/tween.js' 4 | function bounceOut (n) { 5 | if (n < 1 / 2.75) { 6 | return 7.5625 * n * n 7 | } else if (n < 2 / 2.75) { 8 | return 7.5625 * (n -= 1.5 / 2.75) * n + 0.75 9 | } else if (n < 2.5 / 2.75) { 10 | return 7.5625 * (n -= 2.25 / 2.75) * n + 0.9375 11 | } else { 12 | return 7.5625 * (n -= 2.625 / 2.75) * n + 0.984375 13 | } 14 | } 15 | Vue.use(Tweezing, { 16 | tweenjs: tweenjsHelper(TWEEN), 17 | custom (value, end, opts, vm) { 18 | if (vm._unwatchCustom) vm._unwatchCustom() 19 | // TODO custom stop function? 20 | vm._unwatchCustom = vm.$watch( 21 | '$attrs.time', 22 | time => { 23 | opts.$setValue(bounceOut(time)) 24 | }, 25 | { immediate: true } 26 | ) 27 | }, 28 | }) 29 | 30 | function animate (time) { 31 | requestAnimationFrame(animate) 32 | TWEEN.update(time) 33 | } 34 | if (process.client) requestAnimationFrame(animate) 35 | -------------------------------------------------------------------------------- /components/three/Renderer.js: -------------------------------------------------------------------------------- 1 | import { WebGLRenderer, BasicShadowMap } from 'three' 2 | 3 | export default { 4 | props: { 5 | width: Number, 6 | height: Number, 7 | }, 8 | 9 | provide () { 10 | this.renderer = new WebGLRenderer() 11 | this.renderer.shadowMap.enabled = true 12 | this.renderer.shadowMap.type = BasicShadowMap 13 | return { 14 | renderer: this, 15 | } 16 | }, 17 | 18 | mounted () { 19 | this.renderer.setSize(this.width, this.height) 20 | this.animate() 21 | }, 22 | 23 | methods: { 24 | animate () { 25 | if (this.scene && this.camera) { 26 | this.renderer.render(this.scene, this.camera) 27 | } else { 28 | // console.warn('You need a scene and a camera inside of the renderer') 29 | } 30 | requestAnimationFrame(this.animate.bind(this)) 31 | }, 32 | }, 33 | 34 | render (h) { 35 | this.$nextTick().then(() => { 36 | this.$el.appendChild(this.renderer.domElement) 37 | }) 38 | return h('div', this.$slots.default) 39 | }, 40 | } 41 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "demos", 3 | "version": "1.0.0", 4 | "description": "State Animations demos", 5 | "author": "Eduardo San Martin Morote ", 6 | "private": true, 7 | "scripts": { 8 | "dev": "nuxt", 9 | "build": "nuxt build", 10 | "start": "nuxt start", 11 | "generate": "nuxt generate", 12 | "deploy": "npm run generate && surge dist --domain https://state-animations.surge.sh", 13 | "lint": "eslint --ext .js,.vue --ignore-path .gitignore .", 14 | "precommit": "npm run lint" 15 | }, 16 | "dependencies": { 17 | "@nuxtjs/pwa": "^2.0.5", 18 | "@tweenjs/tween.js": "^17.2.0", 19 | "animate.css": "^3.6.1", 20 | "lodash": "^4.17.5", 21 | "modern-normalize": "^0.3.0", 22 | "nuxt": "^1.0.0", 23 | "p5": "^0.6.0", 24 | "randomcolor": "^0.5.3", 25 | "three": "^0.92.0", 26 | "vue-global-events": "^1.0.2", 27 | "vue-motion": "^0.2.2", 28 | "vue-tweezing": "^0.1.2" 29 | }, 30 | "devDependencies": { 31 | "babel-eslint": "^8.2.1", 32 | "eslint": "^4.15.0", 33 | "eslint-config-posva": "^1.3.2", 34 | "eslint-friendly-formatter": "^3.0.0", 35 | "eslint-loader": "^1.7.1", 36 | "eslint-plugin-vue": "^4.0.0", 37 | "node-sass": "^4.7.2", 38 | "postcss-nested": "^3.0.0", 39 | "sass-loader": "^6.0.6" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /nuxt.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | /* 3 | ** Headers of the page 4 | */ 5 | head: { 6 | title: 'Vue.js State Animations Demos', 7 | meta: [ 8 | { charset: 'utf-8' }, 9 | { hid: 'description', name: 'description', content: 'State Animations demos' }, 10 | ], 11 | link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }], 12 | }, 13 | /* 14 | ** Customize the progress bar color 15 | */ 16 | loading: { color: '#eee' }, 17 | /* 18 | ** Build configuration 19 | */ 20 | build: { 21 | /* 22 | ** Run ESLint on save 23 | */ 24 | extend (config, { isDev, isClient }) { 25 | config.resolve.mainFields = ['module', 'main'] 26 | }, 27 | postcss: [require('postcss-nested')()], 28 | }, 29 | 30 | modules: ['@nuxtjs/pwa'], 31 | 32 | plugins: [ 33 | '~/plugins/motion', 34 | '~/plugins/tweezing', 35 | '~/plugins/global-events', 36 | { src: '~plugins/ga.js', ssr: false }, 37 | { src: '~/plugins/three', ssr: false }, 38 | ], 39 | 40 | css: ['modern-normalize/modern-normalize.css', 'animate.css/animate.css', '~/assets/main.css'], 41 | 42 | manifest: { 43 | name: 'State Animations Demos', 44 | short_name: 'State Animations', 45 | lang: 'en', 46 | background_color: '#111111', 47 | theme_color: '#8652e7', 48 | display: 'standalone', 49 | description: 'Demos for my State Animations talk', 50 | }, 51 | } 52 | -------------------------------------------------------------------------------- /pages/sudoku.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 37 | 38 | 66 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # State Animations demos 2 | 3 | > Demos used in my talk State Animations, getting them right 4 | 5 | [See the result online](https://state-animations-amsterdam.surge.sh) 6 | 7 | ## Demos 8 | 9 | ### Basic transition 10 | 11 | Showcase a simple transition usage 12 | 13 | ### Easings graph 14 | 15 | Runtime generated easing graphs using VueTweezing and tween.js 16 | 17 | ### Motion graph 18 | 19 | Runtime generated motion graphs using VueMotion (spring physics) 20 | 21 | ### Polygon 22 | 23 | Demo example for state transitions from the official docs using VueTweezing 24 | 25 | ### Easing with mouse 26 | 27 | Using the mouse position to control the easing function instead of time 28 | 29 | ### Plot 30 | 31 | Dynamically generated svg plot with natural transitions using VueMotion 32 | 33 | ### Sudoku 34 | 35 | Demo example for `transition-group` from the official docs using VueMotion instead 36 | 37 | ### Music 38 | 39 | Bonus example controlling the frame rate of a music with VueMotion or VueTweezing 40 | 41 | ## Testing locally 42 | 43 | ``` bash 44 | # install dependencies 45 | $ npm install # Or yarn install 46 | 47 | # serve with hot reload at localhost:3000 48 | $ npm run dev 49 | ``` 50 | 51 | ## Libraries used 52 | 53 | - [VueMotion](https://github.com/posva/vue-motion) 54 | - [VueTweezing](https://github.com/posva/vue-tweezing) 55 | - [tween.js](https://github.com/tweenjs/tween.js) 56 | - [p5](https://github.com/processing/p5.js) 57 | 58 | ## Conferences 59 | 60 | These demos were shown at Vue.js Amsterdam 16 Feb 2018 61 | 62 | [Slides](https://slides.com/posva/state-animations) 63 | -------------------------------------------------------------------------------- /pages/easing-mouse.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 38 | 39 | 66 | -------------------------------------------------------------------------------- /pages/index.vue: -------------------------------------------------------------------------------- 1 | 34 | 35 | 38 | 39 | 66 | -------------------------------------------------------------------------------- /components/AppLogo.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 80 | -------------------------------------------------------------------------------- /assets/datasets.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "text": "First Dataset", 4 | "value": [ 5 | 25, 6 | 37, 7 | 15, 8 | 13, 9 | 25, 10 | 30, 11 | 11, 12 | 17, 13 | 35, 14 | 10, 15 | 25, 16 | 15, 17 | 5, 18 | 27, 19 | 15, 20 | 13, 21 | 25, 22 | 36, 23 | 15, 24 | 14, 25 | 35, 26 | 10, 27 | 14, 28 | 15, 29 | 35, 30 | 17, 31 | 12, 32 | 13, 33 | 25, 34 | 30, 35 | 14, 36 | 17, 37 | 35, 38 | 10, 39 | 25, 40 | 15 41 | ] 42 | }, 43 | { 44 | "text": "Second Dataset", 45 | "value": [ 46 | 13, 47 | 25, 48 | 30, 49 | 11, 50 | 17, 51 | 35, 52 | 10, 53 | 25, 54 | 15, 55 | 5, 56 | 27, 57 | 15, 58 | 13, 59 | 25, 60 | 36, 61 | 15, 62 | 14, 63 | 35, 64 | 10, 65 | 14, 66 | 15, 67 | 35, 68 | 17, 69 | 12, 70 | 13, 71 | 25, 72 | 30, 73 | 14, 74 | 17, 75 | 35, 76 | 10, 77 | 25, 78 | 15, 79 | 25, 80 | 37, 81 | 15 82 | ] 83 | }, 84 | { 85 | "text": "Third Dataset", 86 | "value": [ 87 | 35, 88 | 10, 89 | 25, 90 | 15, 91 | 5, 92 | 27, 93 | 15, 94 | 13, 95 | 25, 96 | 36, 97 | 15, 98 | 14, 99 | 35, 100 | 10, 101 | 14, 102 | 15, 103 | 35, 104 | 17, 105 | 12, 106 | 13, 107 | 25, 108 | 30, 109 | 14, 110 | 17, 111 | 35, 112 | 10, 113 | 25, 114 | 15, 115 | 25, 116 | 37, 117 | 15, 118 | 13, 119 | 25, 120 | 30, 121 | 11, 122 | 17 123 | ] 124 | } 125 | ] 126 | -------------------------------------------------------------------------------- /components/three/Object3D.js: -------------------------------------------------------------------------------- 1 | // import { Mesh } from 'three' 2 | 3 | function walkGet (obj, path) { 4 | const keys = path.split('.') 5 | while (keys.length) { 6 | obj = obj[keys.shift()] 7 | } 8 | return obj 9 | } 10 | 11 | function walkSet (obj, path, val) { 12 | const keys = path.split('.') 13 | while (keys.length > 1) { 14 | obj = obj[keys.shift()] 15 | } 16 | obj[keys.shift()] = val 17 | } 18 | 19 | function propWatcher (propsToWatch) { 20 | return Object.keys(propsToWatch).reduce((watch, prop) => { 21 | watch[prop] = { 22 | handler (val) { 23 | walkSet(this.obj, propsToWatch[prop], val) 24 | }, 25 | // this is not enough, because obj doesn't exist 26 | // immediate: true, 27 | } 28 | return watch 29 | }, {}) 30 | } 31 | 32 | const watch = propWatcher({ 33 | 'scale.x': 'scale.x', 34 | 'scale.y': 'scale.y', 35 | 'scale.z': 'scale.z', 36 | 37 | 'position.x': 'position.x', 38 | 'position.y': 'position.y', 39 | 'position.z': 'position.z', 40 | 41 | 'rotation.x': 'rotation.x', 42 | 'rotation.y': 'rotation.y', 43 | 'rotation.z': 'rotation.z', 44 | }) 45 | 46 | const VectorType = (v = 0) => ({ 47 | type: Object, 48 | default: () => ({ 49 | x: v, 50 | y: v, 51 | z: v, 52 | }), 53 | }) 54 | 55 | export default { 56 | inject: ['scene'], 57 | 58 | props: { 59 | position: Object, 60 | rotation: Object, 61 | scale: Object, 62 | }, 63 | 64 | mounted () { 65 | if (!this.obj) return 66 | this.scene.add(this.obj) 67 | Object.entries(watch).forEach(([prop, { handler }]) => { 68 | try { 69 | const val = walkGet(this, prop) 70 | if (val == null) return 71 | handler.call(this, val) 72 | } catch (err) {} 73 | }) 74 | }, 75 | 76 | beforeDestroy () { 77 | if (this.obj) this.scene.remove(this.obj) 78 | }, 79 | 80 | render (h) { 81 | return !this.$slots.default ? h() : h('div', this.$slots.default) 82 | }, 83 | 84 | watch, 85 | } 86 | -------------------------------------------------------------------------------- /pages/sudoku-motion.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | 66 | 67 | 92 | -------------------------------------------------------------------------------- /pages/plot-motion.vue: -------------------------------------------------------------------------------- 1 | 42 | 43 | 72 | 73 | 120 | -------------------------------------------------------------------------------- /pages/polygon.vue: -------------------------------------------------------------------------------- 1 | 55 | 56 | 139 | 140 | 157 | -------------------------------------------------------------------------------- /pages/three.vue: -------------------------------------------------------------------------------- 1 | 58 | 59 | 166 | 167 | 188 | -------------------------------------------------------------------------------- /assets/main.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --color-dark: #161616; 3 | --color-ocean: #416dea; 4 | --color-grass: #3dd28d; 5 | --color-snow: #ffffff; 6 | --color-salmon: #f32c52; 7 | --color-sun: #feee7d; 8 | --color-dark-purple: #492789; 9 | --color-purple: #8652e7; 10 | --color-alge: #7999a9; 11 | --color-flower: #353866; 12 | --color-smoke: #e4e4e4; 13 | 14 | --font-face: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 15 | 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; 16 | 17 | /* Must change based on Font */ 18 | --sudoku-cell-size: 32px; 19 | } 20 | 21 | html { 22 | font-family: var(--font-face); 23 | font-size: 22px; 24 | word-spacing: 1px; 25 | -ms-text-size-adjust: 100%; 26 | -webkit-text-size-adjust: 100%; 27 | -moz-osx-font-smoothing: grayscale; 28 | -webkit-font-smoothing: antialiased; 29 | box-sizing: border-box; 30 | background-color: #111111; 31 | color: white; 32 | } 33 | 34 | .container { 35 | padding: 0.3rem; 36 | min-height: 100vh; 37 | display: flex; 38 | width: 100%; 39 | justify-content: center; 40 | align-items: center; 41 | text-align: center; 42 | background-color: #111111; 43 | } 44 | 45 | input[type='text'], 46 | input[type='number'] { 47 | padding: 0.3rem; 48 | border-radius: 0.15rem; 49 | border: 1px solid var(--color-purple); 50 | } 51 | 52 | input[type='number'] { 53 | width: 4rem; 54 | } 55 | 56 | .mb1 { 57 | margin-bottom: 1rem; 58 | } 59 | 60 | p { 61 | margin: 0.8rem auto; 62 | } 63 | 64 | label.text { 65 | display: flex; 66 | width: 100%; 67 | align-items: center; 68 | justify-content: space-around; 69 | margin-bottom: 0.3rem; 70 | } 71 | 72 | input[type='range'] { 73 | display: block; 74 | -webkit-appearance: none; 75 | background-color: var(--color-smoke); 76 | height: 0.3rem; 77 | border-radius: 0.3rem; 78 | margin: calc(0.5rem * (1.3 - 0.3)) auto; 79 | outline: 0; 80 | 81 | &::-webkit-slider-thumb { 82 | -webkit-appearance: none; 83 | background-color: var(--color-purple); 84 | width: 1.3rem; 85 | height: 1.3rem; 86 | border-radius: 50%; 87 | border: 0.15rem solid #111111; 88 | cursor: pointer; 89 | transition: 0.2s ease-in-out; 90 | 91 | &:hover, 92 | &:active { 93 | /* background-color: var(--color-snow); */ 94 | border: 0.15rem solid var(--color-smoke); 95 | } 96 | 97 | &:active { 98 | transform: scale(1.6); 99 | } 100 | } 101 | 102 | &[disabled]::-webkit-slider-thumb { 103 | cursor: not-allowed; 104 | width: 1rem; 105 | height: 1rem; 106 | border: 0.075rem solid var(--color-dark); 107 | background-color: var(--color-smoke); 108 | 109 | &:hover { 110 | border: 0.075rem solid var(--color-dark); 111 | background-color: var(--color-smoke); 112 | } 113 | } 114 | } 115 | 116 | input:active, 117 | input:focus, 118 | input:hover { 119 | color: var(--color-purple); 120 | } 121 | 122 | label { 123 | transition: color 150ms; 124 | &:hover, 125 | &:active, 126 | &:focus { 127 | color: var(--color-purple); 128 | } 129 | } 130 | 131 | label.radio { 132 | cursor: pointer; 133 | } 134 | 135 | input[type='radio'] { 136 | -webkit-appearance: none; 137 | -moz-appearance: none; 138 | appearance: none; 139 | border-radius: 50%; 140 | width: 0.8rem; 141 | height: 0.8rem; 142 | border: 0.4rem solid var(--color-smoke); 143 | background-color: var(--color-purple); 144 | transition: border 150ms; 145 | 146 | cursor: pointer; 147 | 148 | &:checked { 149 | border: 0.15rem solid var(--color-smoke); 150 | } 151 | } 152 | 153 | button { 154 | overflow: hidden; 155 | 156 | margin: 0.6rem; 157 | padding: 0.75rem; 158 | 159 | cursor: pointer; 160 | user-select: none; 161 | text-align: center; 162 | white-space: nowrap; 163 | text-decoration: none !important; 164 | text-transform: none; 165 | text-transform: capitalize; 166 | 167 | background-color: var(--color-purple); 168 | color: var(--color-snow); 169 | border: 0.15rem solid var(--color-purple); 170 | border-radius: 0.3rem; 171 | 172 | font-size: 1rem; 173 | font-weight: 500; 174 | line-height: 1.3; 175 | 176 | -webkit-appearance: none; 177 | -moz-appearance: none; 178 | appearance: none; 179 | 180 | min-width: 10rem; 181 | text-align: center; 182 | 183 | box-shadow: 2px 5px 10px var(--color-dark); 184 | 185 | transition-property: border-color, color, background-color, opacity; 186 | transition-duration: 150ms; 187 | 188 | &:hover { 189 | background-color: var(--color-purple); 190 | border: 0.15rem solid var(--color-snow); 191 | } 192 | 193 | &:active { 194 | color: var(--color-purple); 195 | background-color: var(--color-smoke); 196 | } 197 | } 198 | 199 | canvas[id^='defaultCanvas'] { 200 | display: none; 201 | visibility: hidden; 202 | } 203 | 204 | select { 205 | background: var(--color-smoke); 206 | color: var(--color-dark-purple); 207 | border: 0.05rem solid var(--color-dark-purple); 208 | outline: 0; 209 | cursor: pointer; 210 | height: 1.5rem; 211 | 212 | transition: color 150ms; 213 | 214 | &:hover { 215 | color: var(--color-purple); 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /components/music.vue: -------------------------------------------------------------------------------- 1 | 74 | 75 | 188 | 189 | 212 | -------------------------------------------------------------------------------- /motions/App.vue: -------------------------------------------------------------------------------- 1 | 52 | 53 | 178 | 179 | 268 | -------------------------------------------------------------------------------- /pages/easings-graph.vue: -------------------------------------------------------------------------------- 1 | 51 | 52 | 179 | 180 | 279 | -------------------------------------------------------------------------------- /pages/motion-graph.vue: -------------------------------------------------------------------------------- 1 | 51 | 52 | 180 | 181 | 284 | -------------------------------------------------------------------------------- /pizzi.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | It sounds to me! 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 | 22 |
23 |
{{ currentSound }}: {{ sound }}
24 |
Current values
25 |
 26 |           Frequency: {{ frequency }}
 27 |           Volume: {{ volume }}
 28 |         
29 | 30 | 31 |
32 |
33 | 34 |
Target values
35 |
 36 |       FrequencyFactor: {{ frequencyFactor }}
 37 |       Speed: {{ timeMultiplicator }}
 38 |       Volume: {{ volume }}
 39 |     
40 | 41 |
42 | 45 | 46 | 49 | 50 | 53 | 54 | 57 |
58 | 59 |
60 | 63 | 64 | 67 |
68 | 🔈 69 | 70 |
👴 71 | 👦 72 |
⏳ 73 | 74 | 75 |
76 | 77 | 374 | 375 | 376 | 377 | 378 | --------------------------------------------------------------------------------