UNPKG

fullpage-vue

Version:

vue 2.x fullpage rollup

507 lines (455 loc) 14.6 kB
/** * vue2.x fullpage */ import { on, off, triggerEvent } from './event'; function getCurrentStyle(obj, prop) { if (obj.currentStyle) { return obj.currentStyle[prop]; } else if (window.getComputedStyle) { let propprop = prop.replace(/([A-Z])/g, "-$1"); propprop = prop.toLowerCase(); return document.defaultView.getComputedStyle(obj, null)[prop]; } return null; } function slice(...args) { const [that, ...other] = args; return Array.prototype.slice.apply(that, other) } class Fullpage { constructor(el, options, vnode) { this.assignOpts(options); this.vnode = vnode; this.vm = vnode.context; this.startY = 0; this.opts.movingFlag = false; this.el = el; this.el.$fullpage = this; this.el.classList.add(this.opts.classPrefix); this.parentEle = this.el.parentNode; this.parentEle.classList.add("fullpage-container"); this.pageEles = this.el.children; this.total = this.pageEles.length; this.direction = -1; this.curIndex = this.opts.start; this.preIndex = -1; this.disabled = !!this.opts.disabled; this.initScrollDirection(); this.initEvent(el); window.setTimeout(() => { console.log('init', this) this.resize(); var startIndex = this.opts.start; //The first page triggers the animation directly this.moveTo(startIndex, false); this.toogleAnimate(startIndex, true); this.curIndex = startIndex; }, 0); } resize() { this.width = this.opts.width || this.el.offsetWidth; this.height = this.opts.height || this.el.offsetHeight; let i = 0, length = this.pageEles.length, pageEle; for (; i < length; i++) { pageEle = this.pageEles[i]; pageEle.classList.add("page"); //pageEle.style.width = this.width + 'px' pageEle.style.height = this.height + "px"; } } correct() { //Correct position after onresize if (this.current === 0) { return; } let dist = this.curIndex * (this.opts.dir === "v" ? -this.height : -this.width); this.move(dist); } setOptions(options) { this.assignOpts(options, this.opts); } /** * 切换动画 * @param {Number} curIndex * @param {Boolean} isAll 是否是其他全部。默认只是关闭上一次,初始时需关闭所有 */ toogleAnimate(curIndex, isAll) { var exitElements = slice(this.pageEles, this.preIndex, this.preIndex + 1); if (isAll) { exitElements = slice(this.pageEles, 0).filter(function(_, index) { return index != curIndex }) } Fullpage.broadcast(slice(this.pageEles, curIndex, curIndex + 1), "toogle.animate", true); Fullpage.broadcast(exitElements, "toogle.animate", false); } assignOpts(opts, o) { for (var key in Fullpage.defaultOptions) { if (!opts.hasOwnProperty(key)) { opts[key] = Fullpage.defaultOptions[key] } } this.opts = opts } initScrollDirection() { if (this.opts.dir !== "v") { this.el.classList.add(this.opts.classPrefix + "-h"); } } destroy() { } initEvent(el) { this.prevIndex = this.curIndex; on(window, "resize", () => { this.resize(); this.correct(); }); if ("ontouchstart" in document) { /// touch /// on(el, "touchstart", e => { if (this.opts.movingFlag) { return false; } this.startX = e.targetTouches[0].pageX; this.startY = e.targetTouches[0].pageY; }); on(el, "touchend", e => { //e.preventDefault(); if (this.opts.movingFlag) { return false; } let preIndex = this.curIndex; let dir = this.opts.dir; let sub = (this.direction = dir === "v" ? (e.changedTouches[0].pageY - this.startY) / this.height : (e.changedTouches[0].pageX - this.startX) / this.width); let der = sub > this.opts.der ? -1 : sub < -this.opts.der ? 1 : 0; let curIndex = der + this.curIndex; this.moveTo(curIndex, true); }); on(document.body, "touchmove", e => { let { overflow } = this.opts; let currentPage = this.pageEles[this.curIndex]; if (overflow === "hidden") { //e.preventDefault(); return false; } else { let currentTarget = e.target; while (currentTarget) { if ( (overflow === "scroll" && currentTarget === currentPage) || (overflow !== "scroll" && currentTarget !== currentPage) ) { if (!Fullpage.iSWhetherEnds( currentTarget, this.direction )) { return; } } currentTarget = currentTarget.parentNode; } e.preventDefault(); } }); } //else { let isMousedown = false; on(el, "mousedown", e => { if (this.opts.movingFlag) { return false; } isMousedown = true; this.startX = e.pageX; this.startY = e.pageY; }); on(el, "mouseup", e => { isMousedown = false; }); on(el, "mousemove", e => { // @TODO The same direction requires the last slide to bubble //e.preventDefault(); if (this.opts.movingFlag || !isMousedown) { return false; } let dir = this.opts.dir; let sub = (this.direction = dir === "v" ? (e.pageY - this.startY) / this.height : (e.pageX - this.startX) / this.width); let der = sub > this.opts.der ? -1 : sub < -this.opts.der ? 1 : 0; let curIndex = der + this.curIndex; this.moveTo(curIndex, true); }); let debounceTimer, interval = 1200, debounce = true; // fixed firefox DOMMouseScroll closed #1. let mousewheelType = document.mozFullScreen !== undefined ? "DOMMouseScroll" : "mousewheel"; on(el, mousewheelType, e => { if (this.opts.movingFlag) { return false; } if (!debounce) { return; } debounce = false; clearTimeout(debounceTimer); debounceTimer = setTimeout(() => { debounce = true; }, interval); let dir = this.opts.dir; // Compatible DOMMouseScroll event.detail // see http://www.javascriptkit.com/javatutors/onmousewheel.shtml let detail = e.wheelDelta ? e.wheelDelta / 120 : e.detail * -1 //let detail = e.detail ? e.detail * -120 : e.wheelDelta; //Only support Y let der = this.direction = detail > 0 ? -1 : detail < 0 ? 1 : 0; let curIndex = der + this.curIndex; this.moveTo(curIndex, true); if (e.preventDefault) { //disable default wheel action of scrolling page e.preventDefault(); } else { return false; } }); //} on(window, "resize", () => { if (el.offsetHeight != this.height) { this.resize(); } }); } move(dist) { let xPx = 0, yPx = 0; if (this.opts.dir === "v") { yPx = dist; } else { xPx = dist; } this.el.style.cssText += ";-webkit-transform : translate3d(" + xPx + "px, " + yPx + "px, 0px);" + "transform : translate3d(" + xPx + "px, " + yPx + "px, 0px);"; } /** * * @param {Number} moveToIndex Move to index * @param {Boolean} animated Animated move? * @param {Boolean} force Fore move, ignore disable default:false */ moveTo(moveToIndex, animated, force) { if(!Number.isInteger(moveToIndex)){ return } if (!force && ( this.opts.overflow === "scroll" && !Fullpage.iSWhetherEnds( this.pageEles[this.curIndex], this.direction ) || (animated && this.disabled === true))) { return; } // no change if (this.curIndex === moveToIndex) { return; } if (!(moveToIndex >= 0 && moveToIndex < this.total)) { if (!!this.opts.loop) { moveToIndex = this.curIndex = moveToIndex < 0 ? this.total - 1 : 0; } else { this.curIndex = moveToIndex < 0 ? 0 : this.total - 1; return; } } //beforeChange return false cancel slide let flag = this.opts.beforeChange.call( this, this.pageEles[this.curIndex], this.curIndex, moveToIndex ); if (flag === false) { return false; } let dist = this.opts.dir === "v" ? moveToIndex * -this.height : moveToIndex * -this.width; this.preIndex = this.curIndex; this.curIndex = moveToIndex; let fired = false; let wrappedCallback = () => { off( this.el, "webkitTransitionEnd", wrappedCallback ); this.toogleAnimate(this.curIndex); this.opts.afterChange.call( this, this.pageEles[this.curIndex], this.curIndex ); this.opts.movingFlag = false; fired = true; }; if (animated) { this.el.classList.add(this.opts.animateClass); this.opts.movingFlag = true; let transition = getCurrentStyle( document.querySelector("." + this.opts.classPrefix), "transition" ); let duration = this.opts.duration || parseFloat(transition.split(" ")[1]) || 0; on(this.el, "webkitTransitionEnd", wrappedCallback); setTimeout(() => { if (fired) return; wrappedCallback(); }, duration * 1000 + 25); } else { this.el.classList.remove(this.opts.animateClass); } this.move(dist); } movePrev() { this.moveTo(this.curIndex - 1, true); } moveNext() { this.moveTo(this.curIndex + 1, true); } update() { this.pageEles = this.el.children; this.total = this.pageEles.length; this.resize(); } setDisabled(disabled) { this.disabled = disabled } destroy() {} } Fullpage.iSWhetherEnds = (target, direction) => { if (direction < 0) { return target.scrollTop <= 0; } else { let height = target.getBoundingClientRect().height; //@TODO wechat devtool v0.7.0 scrollTop 1px less than actual return target.scrollTop + height > target.scrollHeight - 1; // down } }; Fullpage.broadcast = (elements, eventName, isShow, ancestor) => { elements = slice(elements, 0) if (elements) { elements.forEach((ele) => { if (ele) { // Non cross level broadcast if (ele.classList.contains('fullpage-container') && isShow) { if (isShow) { let $fullpage = ele.querySelector('.fullpage-wp').$fullpage; if ($fullpage) { $fullpage.toogleAnimate($fullpage.curIndex, true) } } } else { ele.dispatchEvent(triggerEvent(eventName, isShow)) Fullpage.broadcast(ele.children, eventName, isShow); } } }) } } Fullpage.defaultOptions = { start: 0, duration: 500, loop: false, /** * direction * h: horizontal * v: vertical */ dir: "v", /** * der * The proportion of move * e.g. * container height = 100 * moving distance >= 100 * der (default:0.1) */ der: 0.1, /** * * @property {boolean} defualt:false */ movingFlag: false, /** * Callback before change * @params * element {Element} current element * currenIndex {Number} current number * next {Number} next nummober * * @type {Boolean} */ beforeChange: noop, /** * Callback after change * * @function * @params * element {Element} current element * currenIndex {Number} current number * * @type {Boolean} */ afterChange: noop, /** * Animate class * @property {string} */ animateClass: "anim", /* * There are scroll bars in the page, * `auto` Detect any element in page * `scroll` Only detect current page * `hidden` ignores the scroll bar in the page * @default hidden */ overflow: "hidden", /** * disabled * @property {boolean} default: false */ disabled: false, classPrefix: 'fullpage-wp' }; function noop() {} export default Fullpage;