634 |
635 | This program is free software: you can redistribute it and/or modify
636 | it under the terms of the GNU Affero General Public License as published by
637 | the Free Software Foundation, either version 3 of the License, or
638 | (at your option) any later version.
639 |
640 | This program is distributed in the hope that it will be useful,
641 | but WITHOUT ANY WARRANTY; without even the implied warranty of
642 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
643 | GNU Affero General Public License for more details.
644 |
645 | You should have received a copy of the GNU Affero General Public License
646 | along with this program. If not, see .
647 |
648 | Also add information on how to contact you by electronic and paper mail.
649 |
650 | If your software can interact with users remotely through a computer
651 | network, you should also make sure that it provides a way for users to
652 | get its source. For example, if your program is a web application, its
653 | interface could display a "Source" link that leads users to an archive
654 | of the code. There are many ways you could offer source, and different
655 | solutions will be better for different programs; see section 13 for the
656 | specific requirements.
657 |
658 | You should also get your employer (if you work as a programmer) or school,
659 | if any, to sign a "copyright disclaimer" for the program, if necessary.
660 | For more information on this, and how to apply and follow the GNU AGPL, see
661 | .
662 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Prezento
2 | ========
3 | Getting Started
4 | With prezento you can show your web designs in a new, interactive way. Show your visitors that you've created a responsive design, choose the device the design should be showcased on and you're set. Here is how:
5 |
6 |
7 |
8 | <!-- include jQuery -->
9 | <script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.js"></script>
10 |
11 | <!-- include prezento -->
12 | <script src="http://path/to/your/copy/of/jquery.prezento.js"></script>
13 |
14 | <script>
15 | $('.prezento-holder').prezento({
16 | //see below for settings
17 | });
18 | </script>
19 |
20 | ...
21 |
22 | <!-- declare a prezento placeholder-->
23 | <div class="prezento-holder"></div>
24 |
25 |
26 |
27 | Usage
28 | You need to declare at least one device in your plugin settings, if you forget to do so you will see an error message with a link, pointing to this readme.
29 |
30 |
31 | <script>
32 | $('.prezento-holder').prezento({
33 | devices: [{
34 | name: 'imac',
35 | deviceImageSRC: 'imac.png',
36 | xLeftTop: 186,
37 | yLeftTop: 111,
38 | xRightBottom: 2106,
39 | yRightBottom: 1261,
40 | breakpoint: 1024,
41 | bgImgSrc : 'your-web-design.jpg',
42 | bgTransitionDuration: '8s'
43 | },{
44 | name: 'ipad',
45 | deviceImageSRC: 'ipad.png',
46 | xLeftTop: 112,
47 | yLeftTop: 110,
48 | xRightBottom: 890,
49 | yRightBottom: 1144,
50 | breakpoint: 480,
51 | bgImgSrc : 'your-web-design-for-tablet.jpg',
52 | bgTransitionDuration: '4s'
53 | },{
54 | name: 'iphone',
55 | deviceImageSRC: 'iphone.png',
56 | xLeftTop: 42,
57 | yLeftTop: 135,
58 | xRightBottom: 439,
59 | yRightBottom: 829,
60 | breakpoint: 0,
61 | bgImgSrc : 'your-web-design-for-mobile.jpg',
62 | bgTransitionDuration: '2s'
63 | }],
64 | responsive: 'window'
65 | });
66 | </script>
67 |
68 |
69 |
70 | Options
71 |
72 |
73 |
74 | Name |
75 | Options |
76 |
77 |
78 | devices[ ] |
79 |
80 |
81 |
82 |
83 | Name |
84 | Type |
85 | Description |
86 |
87 |
88 |
89 |
90 | name |
91 | string |
92 | Give a unique name to this device, usefull when you want to add controls to let the user change the device (see commands) |
93 |
94 |
95 | deviceImageSRC |
96 | string |
97 | Source of the image of the device |
98 |
99 |
100 | xLeftTop |
101 | int |
102 | X Coordinate of the left top position of the screen |
103 |
104 |
105 | yLeftTop |
106 | int |
107 | Y Coordinate of the left top position of the screen |
108 |
109 |
110 | xRightBottom |
111 | int |
112 | X Coordinate of the right bottom position of the screen |
113 |
114 |
115 | yRightBottom |
116 | int |
117 | Y Coordinate of the right bottom position of the screen |
118 |
119 |
120 | breakpoint |
121 | int |
122 | Screenwidth in pixels. Mobile first, so all screensizes bigger than given value will use this device. |
123 |
124 |
125 | bgImgSrc |
126 | string |
127 | Source of your webdesign you want to use with this device. |
128 |
129 |
130 | bgTransitionDuration |
131 | string |
132 | The duration of the animation in seconds. |
133 |
134 |
135 |
136 | |
137 |
138 |
139 | Name |
140 | Type |
141 | Default |
142 | Description |
143 |
144 |
145 | debug |
146 | boolean |
147 | false |
148 | controls if you want to have some developer output in your console. (It shows the contents of all devices you added) |
149 |
150 |
151 | deviceHolder |
152 | string |
153 | 'deviceholder' |
154 | classname of the device which will be used by the script. A new div will be created inside the showcase holder. |
155 |
156 |
157 | deviceScreen |
158 | string |
159 | 'devicescreen' |
160 | classname of the devicescreen which will be used by the script. A new div will be created inside the showcase holder. |
161 |
162 |
163 | startAfterScroll |
164 | boolean |
165 | false |
166 | If the scrolling of the screen should be triggered based on the position of the users viewport. Could be handy if you have a large page and your prezento is below the viewport onload. |
167 |
168 |
169 | distanceTop |
170 | float or string |
171 | 0.25 or '25%' |
172 | If startAfterScroll is true, what should be the distance from the top of the browser to the div holder. Either a value between 0 - 1 or a percentage between 0% - 100% |
173 |
174 |
175 | resetWhenBelow |
176 | boolean |
177 | false |
178 | If the animation should reset itself when it is out of the viewport |
179 |
180 |
181 | responsive |
182 | string |
183 | 'window' |
184 | If the resize event should be triggered, to show your responsive layout. Can be 'window' (breakpoints based on window size), 'parent' (breakpoints based on parent container size), or 'none' (no resize will happen). |
185 |
186 |
187 | autoPlay |
188 | boolean |
189 | true |
190 | If the animation should start directly after pageload. |
191 |
192 |
193 |
194 | Commands
195 |
196 |
197 |
198 | Command |
199 | Description |
200 | Example Usage |
201 |
202 |
203 |
204 |
205 | play() |
206 | start the animation |
207 |
208 |
209 |
210 | $('.prezento-holder').prezento.play();
211 |
212 | |
213 |
214 |
215 | pause() |
216 | pause the animation |
217 |
218 |
219 |
220 | $('.prezento-holder').prezento.pause();
221 |
222 | |
223 |
224 |
225 | resume() |
226 | resume the animation |
227 |
228 |
229 |
230 | $('.prezento-holder').prezento.resume();
231 |
232 | |
233 |
234 |
235 | reset() |
236 | reset the animation |
237 |
238 |
239 |
240 | $('.prezento-holder').prezento.reset();
241 |
242 | |
243 |
244 |
245 | changeDevice(name) |
246 | Change the device your design is presented on based on the name you have entered for the device |
247 |
248 |
249 |
250 | $('.prezento-holder').prezento.changeDevice('imac');
251 |
252 | |
253 |
254 |
255 |
256 |
257 |
258 | Browser Support
259 |
260 |
261 |
262 |  |
263 |  |
264 |  |
265 |  |
266 |  |
267 |  |
268 |
269 |
270 | ✓ |
271 | ✓ 5+ |
272 | ✓ 4+ |
273 | ✓ 12+ |
274 | ✓ 10+ |
275 | ✓ |
276 |
277 |
278 |
279 |
280 |
281 | Copyright and License
282 | Copyright © Ivaldi (http://ivaldi.nl)
Prezento is licensed under the GNU Affero General Public License Version 3.
--------------------------------------------------------------------------------
/jquery.prezento.css:
--------------------------------------------------------------------------------
1 | /* ============================================
2 | Prezento styling
3 | ============================================ */
4 |
5 | /* Keyframes, needed for the moving background */
6 | @keyframes moveBG {
7 | 0% { background-position: 0% 0%; }
8 | 100% { background-position: 0% 100%; }
9 | }
10 |
11 | @-moz-keyframes moveBG {
12 | 0% { background-position: 0% 0%; }
13 | 100% { background-position: 0% 100%; }
14 | }
15 |
16 | @-webkit-keyframes moveBG {
17 | 0% { background-position: 0% 0%; }
18 | 100% { background-position: 0% 100%; }
19 | }
20 |
21 | @-ms-keyframes moveBG {
22 | 0% { background-position: 0% 0%; }
23 | 100% { background-position: 0% 100%; }
24 | }
25 |
26 | @-o-keyframes moveBG {
27 | 0% { background-position: 0% 0%; }
28 | 100% { background-position: 0% 100%; }
29 | }
30 |
31 | /* Error message for when you forget to add devices */
32 | .pserror{
33 | background-color: #d00022;
34 | color: #fff;
35 | padding: 10px;
36 | text-align:center;
37 | width: 300px;
38 | margin: 0 auto;
39 | }
40 |
41 | .pserror a{
42 | color: #FFF;
43 | text-decoration: underline;
44 | }
--------------------------------------------------------------------------------
/jquery.prezento.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Name: jQuery Prezento
3 | * Original author: @ivaldi
4 | * Licensed under the GNU Affero General Public License Version 3
5 | */
6 |
7 | ;(function($){
8 | "use strict";
9 |
10 | $.fn.prezento = function(options){
11 |
12 | // Set defaults
13 | var config = $.extend({
14 | devices: [],
15 | debug: false,
16 | deviceHolder: 'prezento-device',
17 | deviceScreen: 'prezento-screen',
18 | startAfterScroll : false,
19 | distanceTop: 0.25,
20 | resetWhenBelow: false,
21 | responsive: 'window',
22 | autoPlay: true
23 | }, options ),
24 | parentElem = this,
25 | deviceName = [],
26 | selectedDevice,
27 | currentPlayState;
28 |
29 | // Set value for distanceTop, depends if the user entered a float or string
30 | config.distanceTop = (typeof(config.distanceTop) === 'string') ? parseFloat(config.distanceTop)/100 : config.distanceTop;
31 |
32 | // Create layout
33 | parentElem
34 | .css({'position': 'relative', 'display': 'inline-block', 'margin' : '0 auto', 'width' : '100%', 'text-align' : 'center' })
35 | .append('')
36 | .append('');
37 |
38 |
39 | // Initialization logic
40 | function init(){
41 | sortDevice();
42 | setBreakpoints(config.devices.length);
43 | }
44 |
45 | // Order devices based on breakpoint value, output highest first
46 | function sortDevice(){
47 | config.devices.sort(function(a,b) {
48 | return b.breakpoint - a.breakpoint;
49 | });
50 | }
51 |
52 | // Change layout of screen based on selected width
53 | function setBreakpoints(numberOfDevices){
54 | if(config.responsive === 'parent'){
55 | var windowWidth = parentElem.parent().width();
56 | }else{
57 | var windowWidth = $(window).width();
58 | }
59 | for(var i = 0; i < numberOfDevices; i++){
60 | deviceName[config.devices[i].name] = i;
61 | }
62 | for(var i = 0; i < numberOfDevices; i++){
63 | if(windowWidth - config.devices[i].breakpoint > 0){
64 | selectedDevice = i;
65 | setPlaceholderImage(selectedDevice);
66 | break;
67 | }
68 | }
69 | }
70 |
71 | // Create animation based on device and tell if it should play, pause, resume or reset
72 | function animAction(selectedDevice, motion){
73 | var anim = 'moveBG ' + config.devices[selectedDevice].bgTransitionDuration + ' linear forwards',
74 | position = calcBackgroundPosition(),
75 | duration = ((100-position) / 100) * config.devices[selectedDevice].bgTransitionDuration.replace("s",""),
76 | transi = 'all ' + duration + 's linear';
77 |
78 | if(motion === 'play'){
79 | $('.'+config.deviceScreen).css({
80 | 'animation': anim,
81 | 'transition': 'none'
82 | });
83 | }
84 |
85 | if(motion === 'pause'){
86 | $('.'+config.deviceScreen).css({
87 | 'background-position' : '0% ' + position +'%',
88 | 'animation': 'none',
89 | 'transition': 'none'
90 | });
91 | currentPlayState = 'paused';
92 | }
93 |
94 | if(motion === 'resume'){
95 | if(currentPlayState === 'paused'){
96 | $('.'+config.deviceScreen).css({
97 | 'background-position' : '0% 100%',
98 | 'transition': transi,
99 | });
100 | currentPlayState = 'resumed';
101 | }
102 | }
103 |
104 | if(motion === 'reset'){
105 | $('.'+config.deviceScreen).css({
106 | 'background-position' : '0% 0%',
107 | 'animation': 'none',
108 | 'transition': 'none'
109 | });
110 | }
111 | }
112 |
113 | // Calculate position of background and return value
114 | function calcBackgroundPosition(){
115 | var myPos = $('.' + config.deviceScreen).css("background-position").split(" ");
116 | myPos = myPos[1].replace("%","");
117 |
118 | return myPos;
119 | }
120 |
121 | // Detect scrollposition and trigger animation based on distance top
122 | function scrollScreen(){
123 | if(typeof selectedDevice === 'undefined'){
124 | init();
125 | }
126 |
127 | var deviceTop = parentElem.offset().top,
128 | deviceHeight = parentElem.height(),
129 | winHeight = $(window).height(),
130 | windowScroll = $(document).scrollTop();
131 |
132 | if((deviceTop - windowScroll) / winHeight < config.distanceTop && (deviceTop - windowScroll + deviceHeight) / winHeight > 0){
133 | animAction(selectedDevice, 'play');
134 | }else{
135 | if(config.resetWhenBelow){
136 | animAction(selectedDevice, 'reset');
137 | }
138 | }
139 | }
140 |
141 | // Output debug values
142 | function debugDevice(){
143 | if(!window.console){ window.console = {log: function(){} }; }
144 | if (typeof console != "undefined" && typeof console.debug != "undefined") {
145 | console.log(config);
146 | }
147 | }
148 |
149 | // Create a dummyImage so we can get original size later
150 | function setPlaceholderImage(selectedDevice){
151 | var dummyImage = new Image();
152 | dummyImage.src = config.devices[selectedDevice].deviceImageSRC;
153 | dummyImage.onload = function(){
154 | initDeviceShowcase(selectedDevice, dummyImage);
155 | }
156 |
157 | // Reset values to default
158 | $('.'+config.deviceScreen).css({
159 | 'background-position' : '0% 0%',
160 | 'animation': 'none',
161 | 'transition': 'none'
162 | });
163 | }
164 |
165 | // Start calculation based on screensize
166 | function initDeviceShowcase(selectedDevice, dummyImage){
167 | var device = config.devices[selectedDevice],
168 | deviceHolder = '.' + config.deviceHolder,
169 | deviceScreen = '.' + config.deviceScreen,
170 | imageWidth = dummyImage.naturalWidth,
171 | imageHeight = dummyImage.naturalHeight;
172 |
173 | $(deviceHolder).html($(dummyImage).clone());
174 |
175 |
176 | $(deviceHolder).find('img').load(function(){
177 | var deviceWidth = $(this).width(),
178 | deviceHeight = $(this).height(),
179 | screenWidth = device.xRightBottom - device.xLeftTop,
180 | screenHeight = device.yRightBottom - device.yLeftTop,
181 | wRatio = screenWidth / imageWidth,
182 | hRatio = screenHeight / imageHeight,
183 | lRatio = device.xLeftTop / imageWidth,
184 | tRatio = device.yLeftTop / imageHeight;
185 |
186 | // Add some basic styling
187 | $(deviceHolder).css({
188 | 'position' : 'relative',
189 | 'z-index' : 3
190 | });
191 |
192 | $(deviceScreen).css({
193 | 'position' : 'absolute',
194 | 'z-index' : 2,
195 | 'width' : Math.ceil(deviceWidth * wRatio),
196 | 'height': Math.ceil(deviceHeight * hRatio),
197 | 'left' : Math.round(deviceWidth * lRatio),
198 | 'top' : Math.round(deviceHeight * tRatio),
199 | 'background-size' : '100%',
200 | 'background-image' : 'url('+device.bgImgSrc+')',
201 | 'background-position' : '0% 0%'
202 | });
203 |
204 | });
205 |
206 | // Extra options based on config
207 | if(!config.startAfterScroll && config.autoPlay){
208 | animAction(selectedDevice, 'play');
209 | }
210 | }
211 |
212 | if(config.devices.length != 0){
213 | // Init plugin
214 | if(config.debug){
215 | $(document).on("ready", debugDevice);
216 | }
217 | if(config.startAfterScroll){
218 | $(window).on("scroll", scrollScreen);
219 | }
220 | if(config.responsive === 'window' || config.responsive === 'parent'){
221 | $(window).on("resize", init);
222 | }
223 | $(window).on("load", init);
224 | }else{
225 | parentElem.append('You haven\'t defined any devices. Please read the instructions on how to do this. At least one device is needed for this plugin to work.
')
226 | }
227 |
228 | // Extend plugin with new functions
229 | $.extend($.fn.prezento, {
230 | play: function(){
231 | animAction(selectedDevice, 'play');
232 | },
233 | pause: function(){
234 | animAction(selectedDevice, 'pause');
235 | },
236 | resume: function(){
237 | animAction(selectedDevice, 'resume');
238 | },
239 | reset: function(){
240 | animAction(selectedDevice, 'reset');
241 | },
242 | changeDevice: function(name){
243 | setPlaceholderImage(deviceName[name]);
244 | }
245 | });
246 | }
247 |
248 | }(jQuery));
--------------------------------------------------------------------------------
/jquery.prezento.min.css:
--------------------------------------------------------------------------------
1 | @keyframes moveBG{0%{background-position:0 0}100%{background-position:0 100%}}@-moz-keyframes moveBG{0%{background-position:0 0}100%{background-position:0 100%}}@-webkit-keyframes moveBG{0%{background-position:0 0}100%{background-position:0 100%}}@-ms-keyframes moveBG{0%{background-position:0 0}100%{background-position:0 100%}}@-o-keyframes moveBG{0%{background-position:0 0}100%{background-position:0 100%}}.pserror{background-color:#d00022;color:#fff;padding:10px;text-align:center;width:300px;margin:0 auto}.pserror a{color:#FFF;text-decoration:underline}
--------------------------------------------------------------------------------
/jquery.prezento.min.js:
--------------------------------------------------------------------------------
1 | (function(e){"use strict";e.fn.prezento=function(t){function u(){a();f(n.devices.length)}function a(){n.devices.sort(function(e,t){return t.breakpoint-e.breakpoint})}function f(t){if(n.responsive==="parent"){var o=r.parent().width()}else{var o=e(window).width()}for(var u=0;u0){s=u;d(s);break}}}function l(t,r){var i="moveBG "+n.devices[t].bgTransitionDuration+" linear forwards",s=c(),u=(100-s)/100*n.devices[t].bgTransitionDuration.replace("s",""),a="all "+u+"s linear";if(r==="play"){e("."+n.deviceScreen).css({animation:i,transition:"none"})}if(r==="pause"){e("."+n.deviceScreen).css({"background-position":"0% "+s+"%",animation:"none",transition:"none"});o="paused"}if(r==="resume"){if(o==="paused"){e("."+n.deviceScreen).css({"background-position":"0% 100%",transition:a});o="resumed"}}if(r==="reset"){e("."+n.deviceScreen).css({"background-position":"0% 0%",animation:"none",transition:"none"})}}function c(){var t=e("."+n.deviceScreen).css("background-position").split(" ");t=t[1].replace("%","");return t}function h(){if(typeof s==="undefined"){u()}var t=r.offset().top,i=r.height(),o=e(window).height(),a=e(document).scrollTop();if((t-a)/o0){l(s,"play")}else{if(n.resetWhenBelow){l(s,"reset")}}}function p(){if(!window.console){window.console={log:function(){}}}if(typeof console!="undefined"&&typeof console.debug!="undefined"){console.log(n)}}function d(t){var r=new Image;r.src=n.devices[t].deviceImageSRC;r.onload=function(){v(t,r)};e("."+n.deviceScreen).css({"background-position":"0% 0%",animation:"none",transition:"none"})}function v(t,r){var i=n.devices[t],s="."+n.deviceHolder,o="."+n.deviceScreen,u=r.naturalWidth,a=r.naturalHeight;e(s).html(e(r).clone());e(s).find("img").load(function(){var t=e(this).width(),n=e(this).height(),r=i.xRightBottom-i.xLeftTop,f=i.yRightBottom-i.yLeftTop,l=r/u,c=f/a,h=i.xLeftTop/u,p=i.yLeftTop/a;e(s).css({position:"relative","z-index":3});e(o).css({position:"absolute","z-index":2,width:Math.ceil(t*l),height:Math.ceil(n*c),left:Math.round(t*h),top:Math.round(n*p),"background-size":"100%","background-image":"url("+i.bgImgSrc+")","background-position":"0% 0%"})});if(!n.startAfterScroll&&n.autoPlay){l(t,"play")}}var n=e.extend({devices:[],debug:false,deviceHolder:"prezento-device",deviceScreen:"prezento-screen",startAfterScroll:false,distanceTop:.25,resetWhenBelow:false,responsive:"window",autoPlay:true},t),r=this,i=[],s,o;n.distanceTop=typeof n.distanceTop==="string"?parseFloat(n.distanceTop)/100:n.distanceTop;r.css({position:"relative",display:"inline-block",margin:"0 auto",width:"100%","text-align":"center"}).append('').append('');if(n.devices.length!=0){if(n.debug){e(document).on("ready",p)}if(n.startAfterScroll){e(window).on("scroll",h)}if(n.responsive==="window"||n.responsive==="parent"){e(window).on("resize",u)}e(window).on("load",u)}else{r.append('You haven\'t defined any devices. Please read the instructions on how to do this. At least one device is needed for this plugin to work.
')}e.extend(e.fn.prezento,{play:function(){l(s,"play")},pause:function(){l(s,"pause")},resume:function(){l(s,"resume")},reset:function(){l(s,"reset")},changeDevice:function(e){d(i[e])}})}})(jQuery)
--------------------------------------------------------------------------------