├── .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 | image1 15 | image2 16 |
17 | 18 | **js** 19 | 20 | 27 | 28 | 29 | 30 | ## 注意事项 31 | 32 | 图片的宽高必须与容器的宽高相同,否则会出现错位的情况。 33 | 34 | ## 切换列表 35 | 36 | 会动态创建在container容器内 37 | 38 | 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 | image1 33 | image2 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); --------------------------------------------------------------------------------