├── .gitignore
├── .DS_Store
├── image
├── 1.png
├── 2.png
└── 3.png
├── src
├── .DS_Store
└── src.js
├── README.md
└── index.html
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | .DS_Store
--------------------------------------------------------------------------------
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/andycall/Splash.js/master/.DS_Store
--------------------------------------------------------------------------------
/image/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/andycall/Splash.js/master/image/1.png
--------------------------------------------------------------------------------
/image/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/andycall/Splash.js/master/image/2.png
--------------------------------------------------------------------------------
/image/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/andycall/Splash.js/master/image/3.png
--------------------------------------------------------------------------------
/src/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/andycall/Splash.js/master/src/.DS_Store
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Splash.js
2 | ===========
3 |
4 | 这是一个图片切换插件, 可以实现一些碎玻璃的效果
5 |
6 |
7 | ## Usage
8 |
9 | 实现图片切换只需以下的简单代码
10 |
11 | **html**
12 |
13 |
14 |

15 |

16 |
17 |
18 | **js**
19 |
20 |
27 |
28 |
29 |
30 | ## 注意事项
31 |
32 | 图片的宽高必须与容器的宽高相同,否则会出现错位的情况。
33 |
34 | ## 切换列表
35 |
36 | 会动态创建在container容器内
37 |
38 |
39 | - 1
40 | - 2
41 | - 3
42 |
43 |
44 | 根据图片的数量来生成li,并添加select类, 当前被选取的图片的类为selected.
45 | ul 和 li 的样式需要自己去设定.
46 |
47 |
48 |
49 |
50 |
51 | ### 可选参数:
52 | 1. cube_map // 容器内小方块的数量,总共为 cube_map * cube_map个
53 | 2. isContinue // 是否轮播
54 | 3. duration // 动画结束后的时间间隔
55 | 4. speed // 动画的时间
56 | 5. index // 动画开始时的索引
57 | 6. isContinue // 切换是不是需要连播
58 | 7. transitionEnd: function(){console.log(this)} // 动画结束后触发的回调函数, this指向为当前的li对象
59 |
60 |
61 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PictureSwap
6 |
29 |
30 |
31 |
32 |

33 |

34 |
35 |
36 |
37 |
38 |
39 |
40 |
56 |
57 |
--------------------------------------------------------------------------------
/src/src.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Splash.js
3 | *
4 | * Splash.js is a simple tool to build some wonderful
5 | * transform styles
6 | *
7 | * ------------------------------------------------
8 | * author: AndyCall
9 | * version: 0.0.1
10 | * source: http://github.com/dongtiangche/Splash.js
11 | * contact : Andy Call
12 | */
13 |
14 |
15 |
16 |
17 | (function(window, undefined) {
18 | "use strict";
19 |
20 | // HELPER FUNCTIONS
21 |
22 | /**
23 | * 获取兼容当前浏览器的属性
24 | */
25 | var pfx = (function() {
26 |
27 | var style = document.createElement('dummy').style,
28 | prefixes = 'Webkit Moz O ms Khtml'.split(' '),
29 | memory = {};
30 |
31 | return function(prop) {
32 | if (typeof memory[prop] === "undefined") {
33 |
34 | var ucProp = prop.charAt(0).toUpperCase() + prop.substr(1),
35 | props = (prop + ' ' + prefixes.join(ucProp + ' ') + ucProp).split(' ');
36 |
37 | memory[prop] = null;
38 | for (var i in props) {
39 | if (style[props[i]] !== undefined) {
40 | memory[prop] = props[i];
41 | break;
42 | }
43 | }
44 |
45 | }
46 |
47 | return memory[prop];
48 | };
49 |
50 | })();
51 |
52 | /**
53 | * 在某些地方需要返回类似-webkit-transform这样的东西
54 | */
55 | var pfx_second = (function(){
56 |
57 | var style = document.createElement('dummy').style,
58 | prefixes = 'Webkit Moz O ms Khtml'.split(' '),
59 | prefix = '-webkit -moz -o -ms -khtml'.split(' '),
60 | memory = {};
61 |
62 | return function(prop) {
63 | if (typeof memory[prop] === "undefined") {
64 |
65 | var ucProp = prop.charAt(0).toUpperCase() + prop.substr(1),
66 | props = (prop + ' ' + prefixes.join(ucProp + ' ') + ucProp).split(' '),
67 | real_prop = (prop + " " + prefix.join('-' + prop + ' ')).split(' ');
68 |
69 | for (var i in props) {
70 | if (style[props[i]] !== undefined) {
71 | memory[prop] = real_prop[i];
72 | break;
73 | }
74 | }
75 |
76 | }
77 |
78 | return memory[prop];
79 | };
80 |
81 |
82 | })();
83 |
84 | /**
85 | * 兼容的属性函数, 对于无法处理的属性,将以数组的形式返回
86 | * @type {css}
87 | */
88 | var css = Splash.prototype.css = function (target, json) {
89 | if (arguments.length < 2) return;
90 |
91 | if (typeof json == 'string') {
92 | json = [json];
93 | }
94 |
95 | var isIE = document.currentStyle,
96 | styleValue = {},
97 | isJSON = isObject(arguments[1]),
98 | isArray = isLikeArray(arguments[1]),
99 | pkey,
100 | unKnown = [],
101 | filter,
102 | RegFilter;
103 |
104 | if (isJSON) {
105 | for (var key in json) {
106 | if (json.hasOwnProperty(key)) {
107 | if (isIE && key == 'opacity') {
108 |
109 | filter = target.currentStyle['filter'];
110 | RegFilter = /(.+opacity=)([0-9]*)([\,\)]?.+)/;
111 |
112 | var StrArr = RegFilter.exec(filter),
113 | strHead = StrArr[1],
114 | strFooter = StrArr[3];
115 |
116 | target.style["filter"] = strHead + json[key] * 100 + strFooter;
117 |
118 | } else {
119 | pkey = pfx(key);
120 | if (pkey != null) {
121 | target.style[pkey] = json[key];
122 | }
123 | else{
124 | unKnown.push([json[key], key]);
125 | }
126 | }
127 | }
128 | }
129 |
130 | return unKnown;
131 |
132 | } else if (isArray) {
133 |
134 | for (var i = 0, len = json.length; i < len; i++) {
135 |
136 | if (isIE) {
137 | if (json[i] == 'opacity') {
138 | RegFilter = /[\,\)]?opacity=([0-9]+)/;
139 | filter = target.currentStyle["filter"];
140 |
141 | styleValue[json[i]] = parseFloat(RegFilter.exec(filter)[1]);
142 | }
143 |
144 | styleValue[json[i]] = target.currentStyle[json[i]];
145 |
146 | } else {
147 |
148 | styleValue[json[i]] = window.getComputedStyle(target, null)[json[i]];
149 | }
150 | }
151 | return styleValue;
152 | }
153 | };
154 |
155 |
156 | /**
157 | * 判断是否是对象
158 | * @param obj
159 | * @returns {boolean}
160 | */
161 | function isObject(obj) {
162 | return Object.prototype.toString.call(obj) === '[object Object]';
163 | }
164 |
165 | /**
166 | * 判断是否是DOM对象
167 | * @param o
168 | * @returns {boolean}
169 | */
170 | function isElement(o) {
171 | return (
172 | typeof HTMLElement === "function" ? o instanceof HTMLElement : //DOM2
173 | o && typeof o === "object" && o !== null && o.nodeType === 1 && typeof o.nodeName === "string"
174 | );
175 | }
176 |
177 | /**
178 | * 判断是否是数组或者类数组
179 | * @param obj
180 | * @returns {*|boolean}
181 | */
182 | function isLikeArray(obj) {
183 | return obj && obj.length >= 0 && typeof obj != 'string';
184 | }
185 |
186 | /**
187 | * 事件添加函数
188 | * @type {on}
189 | */
190 | var on = Splash.prototype.on = function (target, eventName, fn) {
191 | var factor = /\s+/g;
192 | // debugger;
193 | var fnString = fn.toString().replace(factor, "");
194 | if (!target[eventName + "event"]) {
195 | target[eventName + 'event'] = {};
196 | }
197 | target[eventName + 'event'][eventName] = function(e) {
198 | fn.call(this, e);
199 | }
200 | var eventFunc = target[eventName + "event"][eventName];
201 |
202 | if (document.attachEvent) {
203 | target.attachEvent('on' + eventName, eventFunc);
204 | } else if (document.addEventListener) {
205 | target.addEventListener(eventName, eventFunc, false);
206 | } else {
207 | target['on' + eventName] = eventFunc;
208 | }
209 | };
210 |
211 | var rotate = function(r, revert) {
212 | var rX = " rotateX(" + r.x + "deg) ",
213 | rY = " rotateY(" + r.y + "deg) ",
214 | rZ = " rotateZ(" + r.z + "deg) ";
215 |
216 | return revert ? rZ + rY + rX : rX + rY + rZ;
217 | };
218 |
219 | var scale = function(s) {
220 | return " scale(" + s + ") ";
221 | };
222 |
223 | var perspective = function(p) {
224 | return " perspective(" + p + "px) ";
225 | };
226 |
227 | function $(selector) {
228 | return document.querySelector(selector);
229 | }
230 |
231 | function $$(selector) {
232 | return document.querySelectorAll(selector);
233 | }
234 |
235 | function extend(obj, extension) {
236 | for (var key in obj) {
237 | extension[key] = obj[key];
238 | }
239 | return extension;
240 | }
241 |
242 | // Global Variables
243 | var configDefault = {
244 | cube_map: 13, //3行3列
245 | isContinue: true, // 是否连播
246 | duration: 2000, // 500ms
247 | index : 0,
248 | speed : 400,
249 | transitionEnd : function(){console.log(this)}
250 |
251 | },
252 | cube_position = [], // 方块位置缓存数组
253 | imgArr = [], // 图片缓存数组
254 | Index = 0, // 切换索引
255 | container, // 容器
256 | swap = new Swap(), // 摆动器
257 | changeStyle = {x : 0, y : 0, z : 0}, // 切换效果对象
258 | isAnimation = false; // 判断是否正在进行动画
259 |
260 | /**
261 | * 方块构造器
262 | * @param config
263 | * @returns {Array}
264 | */
265 | function cubeConstructor(config) {
266 | var count = config.cube_map * config.cube_map,
267 | cube_map = [config.cube_map, config.cube_map],
268 | ContainerWidth = parseInt(config.width),
269 | ContainerHeight = parseInt(config.height),
270 | cubeWidth = parseFloat((ContainerWidth / cube_map[0]).toFixed(8)),
271 | cubeHeight = parseFloat((ContainerHeight / cube_map[1]).toFixed(8)),
272 | cubeContainer = [],
273 | row = cube_map[0],
274 | col = cube_map[1],
275 | speed = config.speed,
276 | transformPfx = pfx_second("transform"),
277 | pfxTransform = pfx("transform");
278 |
279 | for (var i = 0, len = count; i < len; i++) {
280 | cube_position.push([(i % col) * cubeHeight, (Math.floor(i / row) % row) * cubeWidth]);
281 | var div = {};
282 |
283 | extend({
284 | "position": "absolute",
285 | "display" : "inline-block",
286 | "width": cubeWidth + "px",
287 | 'height': cubeHeight + 'px',
288 | 'top': cube_position[i][0] + 'px',
289 | 'left': cube_position[i][1] + 'px',
290 | 'background': "#fff",
291 | "transition": transformPfx + " " + speed + "ms " + " linear",
292 | "transitionDelay" : 0+ "ms",
293 | 'transformStyle' : 'preserve-3d',
294 | "WebkitBackfaceVisibility": "hidden",
295 | "front" : {
296 | "position" : "absolute",
297 | "width" : cubeWidth + 'px',
298 | "height" : cubeHeight + 'px',
299 | "transform" : "rotateY(0deg)",
300 | "WebkitBackfaceVisibility": "hidden"
301 | },
302 | "back" : {
303 | "position" : "absolute",
304 | "width" : cubeWidth + 'px',
305 | "height" : cubeHeight + 'px',
306 | "transform" : "rotateY(180deg) translateZ(1px)",
307 | "WebkitBackfaceVisibility": "hidden"
308 | }
309 | }, div);
310 |
311 | div[pfxTransform] = rotate(changeStyle);
312 |
313 | cubeContainer.push(div);
314 | }
315 |
316 |
317 | return cubeContainer;
318 | }
319 |
320 |
321 | /**
322 | * 为索引添加click事件
323 | * @param obj
324 | * @private
325 | */
326 | function addButtonEvent (obj){
327 | var index,
328 | self = this,
329 | isContinue = self.config.isContinue,
330 | lis;
331 |
332 | function fireAction(){
333 | index = parseInt(this.innerHTML);
334 | if(isAnimation) return;
335 |
336 | Index = index - 1;
337 |
338 | lis = container.getElementsByTagName('li');
339 |
340 | for(var i = 0,len = lis.length; i < len; i ++){
341 | lis[i].className = "select";
342 | }
343 |
344 | this.className = "selected";
345 |
346 | clearInterval(self._cancelSpeed);
347 |
348 | self.slide(Index, changeStyle, function(){
349 | Index ++;
350 | if(!isContinue) return;
351 |
352 | self.Run(Index);
353 | });
354 | }
355 |
356 | on(obj, 'click', function(){
357 | fireAction.call(this);
358 | });
359 | on(obj,'mouseover', function(){
360 | fireAction.call(this);
361 | })
362 | };
363 |
364 | /**
365 | * 添加索引
366 | * @private
367 | */
368 | function listConstructor(){
369 | var wrapper = document.createElement('ul'),
370 | self = this;
371 |
372 | for(var i = 0,len = imgArr.length; i < len; i ++){
373 | var li = document.createElement('li');
374 | li.className = "select";
375 | li.textContent = i + 1;
376 | addButtonEvent.call(self, li);
377 | wrapper.appendChild(li);
378 | }
379 |
380 | container.appendChild(wrapper);
381 | };
382 |
383 | /**
384 | * 负责为每个小方块设置背景
385 | * @param face
386 | * @param img
387 | * @param config
388 | */
389 | function backgroundConver(face, img, config) {
390 | var row = config.cube_map,
391 | col = config.cube_map,
392 | cubes = $$("." + face),
393 | percentage = [],
394 | percent = 1 / (row - 1);
395 |
396 |
397 | for (var i = 0, len = cubes.length; i < len ; i ++) {
398 |
399 | percentage.push([Math.floor(i / row) * percent, i % col * percent]);
400 |
401 |
402 | var back_position = (percentage[i][0] * 100) + "%" + " " + percentage[i][1] * 100 + "%";
403 |
404 | css(cubes[i], {
405 | 'background': "url(" + img.src + ") no-repeat",
406 | 'backgroundSize': row * 100 + "%",
407 | 'backgroundPosition': back_position
408 | });
409 | }
410 | };
411 |
412 | /**
413 | * 检测容器内的图片,并获取图片的宽高
414 | */
415 | function checkImg(){
416 | var self = this,
417 | config = self.config,
418 | imgs = container.querySelectorAll('img'),
419 | imgSrc = [],
420 | width = 0,
421 | height = 0,
422 | life = 1;
423 |
424 | for(var i = 0; i < imgs.length; i ++){
425 | var img = imgs[i],
426 | styles = css(img,["width", "height"]);
427 |
428 | imgSrc.push(img);
429 | container.removeChild(img);
430 |
431 | if(width != styles.width || height != styles.height){
432 | life--;
433 | }
434 |
435 | if(life < 0) return [];
436 |
437 | width = styles.width;
438 | height = styles.height;
439 |
440 | }
441 |
442 | config.width = width;
443 | config.height = height;
444 |
445 | return imgSrc;
446 | }
447 |
448 | /**
449 | * 最初的更新节点
450 | */
451 | function refreshInit(){
452 | var self = this,
453 | cubeArr = self.cubeArr,
454 | cubeLength = cubeArr.length,
455 | div,
456 | FrageMent = document.createDocumentFragment(),
457 | unKnown,
458 | childStyle,
459 | child,
460 | childType;
461 |
462 |
463 | for(var i = 0; i < cubeLength; i++){
464 | div = document.createElement('div');
465 | div.className = 'cube';
466 |
467 | unKnown = css(div, cubeArr[i]);
468 |
469 | // 说明有无法处理的属性
470 | if(unKnown.length > 0){
471 | unKnown.forEach(function(value, index){
472 |
473 | if(isObject(value[0])){
474 | childStyle = unKnown[index][0];
475 | childType = unKnown[index][1];
476 | child = document.createElement('div');
477 | css(child, childStyle);
478 | child.className = childType;
479 | div.appendChild(child);
480 | }
481 | })
482 | }
483 |
484 | FrageMent.appendChild(div);
485 | }
486 |
487 | container.appendChild(FrageMent);
488 | }
489 |
490 | /**
491 | * 交换工具函数
492 | * @constructor
493 | */
494 | function Swap(){
495 | this.flag = [0,1];
496 | this.index = 0;
497 | }
498 |
499 | Swap.prototype.circle = function(arr){
500 | var self = this,
501 | flag = self.flag,
502 | index = self.index;
503 |
504 | self.index = flag[index] ? 0 : 1;
505 |
506 | return arr[self.index];
507 |
508 | };
509 |
510 | /**
511 | * 初始化
512 | */
513 | Splash.prototype.init = function() {
514 | var config = this.config,
515 | self = this;
516 |
517 | container = this.container;
518 |
519 | imgArr = checkImg.call(self, container);
520 |
521 | if(imgArr.length === 0){
522 | container.innerHTML = "图片的大小不一致!";
523 | return;
524 | }
525 |
526 | css(container, {
527 | 'width': config.width,
528 | 'height': config.height,
529 | 'position': 'relative'
530 | });
531 |
532 | if (!isElement(container)) {
533 | throw new Error('invalid container')
534 | }
535 |
536 | self.cubeArr = cubeConstructor(config);
537 |
538 | refreshInit.call(self);
539 |
540 | listConstructor.call(self);
541 |
542 | backgroundConver.call(self, "front", imgArr[Index], config);
543 | backgroundConver.call(self, 'back', imgArr[Index + 1], config);
544 |
545 | self.Run(Index);
546 |
547 | };
548 |
549 | /**
550 | * 动画入口
551 | * @param index
552 | * @constructor
553 | */
554 | Splash.prototype.Run = function(index){
555 | var self = this,
556 | config = self.config,
557 | cubeArr = self.cubeArr,
558 | isContinue = self.config.isContinue;
559 |
560 | if(isAnimation) return;
561 |
562 | self.slide(Index, changeStyle, function(){
563 | Index++;
564 | if(!isContinue) return;
565 | self.Run(Index);
566 | });
567 | };
568 |
569 | /**
570 | * 下一个图片
571 | */
572 | Splash.prototype.next = function () {
573 | var self = this,
574 | isContinue = self.config.isContinue;
575 |
576 | if(isAnimation) return;
577 |
578 | Index++;
579 |
580 | self.slide(Index, changeStyle, function(){
581 | Index ++;
582 | if(!isContinue) return;
583 | self.Run(Index);
584 | });
585 |
586 | };
587 |
588 | /**
589 | * 上一个图片
590 | */
591 | Splash.prototype.prev = function(){
592 | var self = this,
593 | isContinue = self.config.isContinue;
594 |
595 | if(isAnimation) return;
596 |
597 | Index--;
598 |
599 | self.slide(Index, changeStyle, function(){
600 | Index --;
601 | if(!isContinue) return;
602 | self.Run(Index);
603 | });
604 | };
605 |
606 | /**
607 | * 切换动画
608 | * @param to
609 | * @param moveStyle
610 | * @param callback
611 | */
612 | Splash.prototype.slide = function(to, moveStyle, callback){
613 | var self = this,
614 | config = self.config,
615 | background,
616 | duration = config.duration,
617 | speed = config.speed,
618 | delay = config.delay,
619 | isContinue = config.isContinue;
620 |
621 | changeStyle.y += 180;
622 |
623 |
624 | to == undefined && (to = config.index);
625 |
626 | to < 0 && (to = imgArr.length - 1) || to > (imgArr.length - 1) && (to = 0);
627 |
628 |
629 | Index = to;
630 |
631 | background = swap.circle(['back', 'front']);
632 | backgroundConver.call(self, background, imgArr[to], config);
633 |
634 |
635 | self.move(moveStyle, speed, duration, function(){
636 |
637 | callback.call(self);
638 | });
639 | };
640 |
641 |
642 | /**
643 | * 动画函数
644 | * @param value
645 | */
646 | Splash.prototype.move = function(value, speed, duration, callback) {
647 | var cubes = $$(".cube"),
648 | self = this,
649 | transitionEnd = self.config.transitionEnd;
650 |
651 | isAnimation = true;
652 |
653 | setTimeout(function(){
654 | for (var i = 0, len = cubes.length; i < len; i++) {
655 | css(cubes[i], {
656 | transform: rotate(value)
657 | });
658 | }
659 | },0)
660 |
661 |
662 | if(self._cancelSpeed)
663 | clearTimeout(self._cancelSpeed);
664 |
665 | // 动画停止
666 | setTimeout(function(){
667 | isAnimation = false;
668 |
669 | var lis = container.getElementsByTagName('li');
670 |
671 | for(var i = 0,len = lis.length; i < len; i ++){
672 | lis[i].className = "select";
673 | }
674 |
675 | lis[Index].className = "selected";
676 |
677 | transitionEnd.call(lis[Index]);
678 |
679 | }, speed);
680 |
681 | self._cancelSpeed = setTimeout(function(){
682 |
683 | callback();
684 |
685 | }, speed + duration);
686 |
687 | };
688 |
689 |
690 | function Splash(container, config) {
691 | this.container = container;
692 | this.config = extend(config, configDefault);
693 | this.cubeArr = [];
694 | }
695 |
696 | window.Splash = Splash;
697 |
698 | })(window);
--------------------------------------------------------------------------------