├── .editorconfig ├── .gitignore ├── .jshintrc ├── .npmignore ├── README.md ├── index.js └── package.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 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 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # Compiled binary addons (http://nodejs.org/api/addons.html) 20 | build/Release 21 | 22 | # Dependency directory 23 | # Commenting this out is preferred by some people, see 24 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- 25 | node_modules 26 | 27 | # Users Environment Variables 28 | .lock-wscript 29 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "browser": true, 4 | "devel": true, 5 | "strict": true, 6 | "globalstrict": true, 7 | "curly": true, 8 | "camelcase": false, 9 | "eqeqeq": true, 10 | "indent": 2, 11 | "latedef": true, 12 | "newcap": true, 13 | "noarg": true, 14 | "quotmark": "single", 15 | "regexp": true, 16 | "unused": true, 17 | "undef": true 18 | } 19 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # node-waf configuration 20 | .lock-wscript 21 | 22 | # Compiled binary addons (http://nodejs.org/api/addons.html) 23 | build/Release 24 | 25 | # common 26 | .DS_Store 27 | *.xml 28 | 29 | # do not include test dir in the published module 30 | test 31 | example 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | diaporama-react 2 | ========= 3 | 4 | Implement a React Component to use the [diaporama](https://github.com/gre/diaporama) library. 5 | 6 | [![](https://nodei.co/npm/diaporama-react.png)](https://www.npmjs.com/package/diaporama-react) 7 | 8 | 9 | ```jsx 10 | { 11 | data: PropTypes.object.isRequired, 12 | width: PropTypes.number.isRequired, 13 | height: PropTypes.number.isRequired, 14 | resolution: PropTypes.number, 15 | paused: PropTypes.bool, 16 | loop: PropTypes.bool, 17 | autoplay: PropTypes.bool, 18 | currentTime: PropTypes.number, 19 | playbackRate: PropTypes.number, 20 | onDiaporamaCreated: PropTypes.func // callback giving the diaporama instance. use-case: You can bind Events on the diaporama. See "diaporama" documentation. 21 | } 22 | ``` 23 | 24 | Usage 25 | ----- 26 | 27 | ## app.js 28 | 29 | ```js 30 | import React from 'react'; 31 | import ReactDOM from 'react-dom'; 32 | import Diaporama from 'diaporama-react'; 33 | 34 | const diaporamaData = require('./diaporama.json'); 35 | 36 | let props = { 37 | data: diaporamaData, 38 | width: 320, 39 | height: 240, 40 | loop: true, 41 | autoplay: true, 42 | onDiaporamaCreated: (...args) => { 43 | console.log('onDiaporamaCreated', args); 44 | } 45 | } 46 | 47 | ReactDOM.render(, document.getElementById('app')); 48 | 49 | ``` 50 | 51 | 52 | ## diaporama.json 53 | 54 | Following the [diaporama JSON description schema](https://gre.gitbooks.io/diaporama/content/docs/format.html) 55 | 56 | ```json 57 | { 58 | "timeline": [ 59 | { 60 | "image": "https://crossorigin.me/http://loremflickr.com/320/240/paris", 61 | "duration": 2000, 62 | "kenburns": { 63 | "from": [ 0.5, [0.5, 0.6] ], 64 | "to": [ 1, [0.5, 0.6] ] 65 | }, 66 | "transitionNext": { 67 | "name": "PageCurl", 68 | "duration": 1000 69 | } 70 | },{ 71 | "image": "https://crossorigin.me/http://loremflickr.com/320/240/berlin", 72 | "duration": 2000, 73 | "kenburns": { 74 | "from": [ 0.5, [0.5, 0.6] ], 75 | "to": [ 1, [0.5, 0.6] ] 76 | }, 77 | "transitionNext": { 78 | "name": "PageCurl", 79 | "duration": 1000 80 | } 81 | },{ 82 | "image": "https://crossorigin.me/http://loremflickr.com/320/240/barcelona", 83 | "duration": 2000, 84 | "kenburns": { 85 | "from": [ 0.5, [0.5, 0.6] ], 86 | "to": [ 1, [0.5, 0.6] ] 87 | }, 88 | "transitionNext": { 89 | "name": "PageCurl", 90 | "duration": 1000 91 | } 92 | } 93 | ], 94 | "transitions": [ 95 | { 96 | "glsl": "\n#ifdef GL_ES\nprecision highp float;\n#endif\nuniform sampler2D from,to;uniform float progress;uniform vec2 resolution;const float MIN_AMOUNT=-0.16;const float MAX_AMOUNT=1.3;float amount=progress*(MAX_AMOUNT-MIN_AMOUNT)+MIN_AMOUNT;const float PI=3.141592653589793;const float scale=512.0;const float sharpness=3.0;float cylinderCenter=amount;float cylinderAngle=2.0*PI*amount;const float cylinderRadius=1.0/PI/2.0;vec3 hitPoint(float hitAngle,float yc,vec3 point,mat3 rrotation){float hitPoint=hitAngle/(2.0*PI);point.y=hitPoint;return rrotation*point;}vec4 antiAlias(vec4 color1,vec4 color2,float distanc){distanc*=scale;if(distanc<0.0) return color2;if(distanc>2.0) return color1;float dd=pow(1.0-distanc/2.0,sharpness);return ((color2-color1)*dd)+color1;}float distanceToEdge(vec3 point){float dx=abs(point.x>0.5?1.0-point.x:point.x);float dy=abs(point.y>0.5?1.0-point.y:point.y);if(point.x<0.0) dx=-point.x;if(point.x>1.0) dx=point.x-1.0;if(point.y<0.0) dy=-point.y;if(point.y>1.0) dy=point.y-1.0;if((point.x<0.0||point.x>1.0)&&(point.y<0.0||point.y>1.0)) return sqrt(dx*dx+dy*dy);return min(dx,dy);}vec4 seeThrough(float yc,vec2 p,mat3 rotation,mat3 rrotation){float hitAngle=PI-(acos(yc/cylinderRadius)-cylinderAngle);vec3 point=hitPoint(hitAngle,yc,rotation*vec3(p,1.0),rrotation);if(yc<=0.0&&(point.x<0.0||point.y<0.0||point.x>1.0||point.y>1.0)){vec2 texCoord=gl_FragCoord.xy/resolution.xy;return texture2D(to,texCoord);}if(yc>0.0) return texture2D(from,p);vec4 color=texture2D(from,point.xy);vec4 tcolor=vec4(0.0);return antiAlias(color,tcolor,distanceToEdge(point));}vec4 seeThroughWithShadow(float yc,vec2 p,vec3 point,mat3 rotation,mat3 rrotation){float shadow=distanceToEdge(point)*30.0;shadow=(1.0-shadow)/3.0;if(shadow<0.0) shadow=0.0;else shadow*=amount;vec4 shadowColor=seeThrough(yc,p,rotation,rrotation);shadowColor.r-=shadow;shadowColor.g-=shadow;shadowColor.b-=shadow;return shadowColor;}vec4 backside(float yc,vec3 point){vec4 color=texture2D(from,point.xy);float gray=(color.r+color.b+color.g)/15.0;gray+=(8.0/10.0)*(pow(1.0-abs(yc/cylinderRadius),2.0/10.0)/2.0+(5.0/10.0));color.rgb=vec3(gray);return color;}vec4 behindSurface(float yc,vec3 point,mat3 rrotation){float shado=(1.0-((-cylinderRadius-yc)/amount*7.0))/6.0;shado*=1.0-abs(point.x-0.5);yc=(-cylinderRadius-cylinderRadius-yc);float hitAngle=(acos(yc/cylinderRadius)+cylinderAngle)-PI;point=hitPoint(hitAngle,yc,point,rrotation);if(yc<0.0&&point.x>=0.0&&point.y>=0.0&&point.x<=1.0&&point.y<=1.0&&(hitAngle0.5)){shado=1.0-(sqrt(pow(point.x-0.5,2.0)+pow(point.y-0.5,2.0))/(71.0/100.0));shado*=pow(-yc/cylinderRadius,3.0);shado*=0.5;}else{shado=0.0;}vec2 texCoord=gl_FragCoord.xy/resolution.xy;return vec4(texture2D(to,texCoord).rgb-shado,1.0);}void main(){vec2 texCoord=gl_FragCoord.xy/resolution.xy;const float angle=30.0*PI/180.0;float c=cos(-angle);float s=sin(-angle);mat3 rotation=mat3(c,s,0,-s,c,0,0.12,0.258,1);c=cos(angle);s=sin(angle);mat3 rrotation=mat3(c,s,0,-s,c,0,0.15,-0.5,1);vec3 point=rotation*vec3(texCoord,1.0);float yc=point.y-cylinderCenter;if(yc<-cylinderRadius){gl_FragColor=behindSurface(yc,point,rrotation);return;}if(yc>cylinderRadius){gl_FragColor=texture2D(from,texCoord);return;}float hitAngle=(acos(yc/cylinderRadius)+cylinderAngle)-PI;float hitAngleMod=mod(hitAngle,2.0*PI);if((hitAngleMod>PI&&amount<0.5)||(hitAngleMod>PI/2.0&&amount<0.0)){gl_FragColor=seeThrough(yc,texCoord,rotation,rrotation);return;}point=hitPoint(hitAngle,yc,point,rrotation);if(point.x<0.0||point.y<0.0||point.x>1.0||point.y>1.0){gl_FragColor=seeThroughWithShadow(yc,texCoord,point,rotation,rrotation);return;}vec4 color=backside(yc,point);vec4 otherColor;if(yc<0.0){float shado=1.0-(sqrt(pow(point.x-0.5,2.0)+pow(point.y-0.5,2.0))/0.71);shado*=pow(-yc/cylinderRadius,3.0);shado*=0.5;otherColor=vec4(0.0,0.0,0.0,shado);}else{otherColor=texture2D(from,texCoord);}color=antiAlias(color,otherColor,cylinderRadius-abs(yc));vec4 cl=seeThroughWithShadow(yc,texCoord,point,rotation,rrotation);float dist=distanceToEdge(point);gl_FragColor=antiAlias(color,cl,dist);}", 97 | "uniforms": {}, 98 | "name": "PageCurl" 99 | } 100 | ] 101 | } 102 | ``` 103 | 104 | 105 | Related Projects 106 | --------------- 107 | 108 | - [`diaporama`](https://github.com/gre/diaporama): Image/video/content slideshow engine providing high quality animation effects 109 | - [`diaporama-maker`](https://github.com/gre/diaporama-maker): application to create Diaporama slideshows. 110 | - [`kenburns`](http://github.com/gre/kenburns): KenBurns effect for the Web. 111 | - [`glsl-transition`](https://github.com/glslio/glsl-transition): Perform a GLSL Transition. 112 | - [`slide2d`](https://github.com/gre/slide2d): Express vectorial content in JSON using canvas2d directives. 113 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var React = require('react'); 4 | var Diaporama = require('diaporama'); 5 | var PropTypes = React.PropTypes; 6 | 7 | function affectProps (obj, props) { 8 | for (var k in props) { 9 | if (k !== "onDiaporamaCreated") // blacklisting only for now. We might do whitelist instead ? 10 | obj[k] = props[k]; 11 | } 12 | return obj; 13 | } 14 | 15 | var DiaporamaElement = React.createClass({ 16 | 17 | displayName: 'DiaporamaElement', 18 | 19 | propTypes: { 20 | data: PropTypes.object.isRequired, 21 | width: PropTypes.number.isRequired, 22 | height: PropTypes.number.isRequired, 23 | resolution: PropTypes.number, 24 | paused: PropTypes.bool, 25 | loop: PropTypes.bool, 26 | autoplay: PropTypes.bool, 27 | currentTime: PropTypes.number, 28 | playbackRate: PropTypes.number, 29 | onDiaporamaCreated: PropTypes.func // callback giving the diaporama instance. use-case: You can bind Events on the diaporama. See "diaporama" documentation. 30 | }, 31 | 32 | componentDidMount: function () { 33 | var opts = affectProps({}, this.props); 34 | this.diaporama = Diaporama(this.refs.container, opts); 35 | if (this.props.onDiaporamaCreated) { 36 | this.props.onDiaporamaCreated(this.diaporama); 37 | } 38 | }, 39 | 40 | componentWillUnmount: function () { 41 | this.diaporama.destroy(); 42 | }, 43 | 44 | componentWillReceiveProps: function (props) { 45 | affectProps(this.diaporama, props); 46 | }, 47 | 48 | shouldComponentUpdate: function () { 49 | return false; 50 | }, 51 | 52 | render: function () { 53 | return (
); 54 | } 55 | }); 56 | 57 | module.exports = DiaporamaElement; 58 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "diaporama-react", 3 | "version": "2.1.0", 4 | "description": "Diaporama component for React", 5 | "main": "index.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "git@github.com:glslio/diaporama-react.git" 9 | }, 10 | "keywords": [ 11 | "diaporama", 12 | "react" 13 | ], 14 | "author": "Gaëtan Renaudeau", 15 | "license": "ISC", 16 | "bugs": { 17 | "url": "https://github.com/glslio/diaporama-react/issues" 18 | }, 19 | "homepage": "https://github.com/glslio/diaporama-react", 20 | "browserify": { 21 | "transform": [ 22 | "babelify" 23 | ] 24 | }, 25 | "peerDependencies": { 26 | "react": "*" 27 | }, 28 | "dependencies": { 29 | "babelify": "^6.1.3", 30 | "diaporama": "^2.0.0" 31 | } 32 | } 33 | --------------------------------------------------------------------------------