123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565 |
- /**
- * off-canvas
- * @param {type} $
- * @param {type} window
- * @param {type} document
- * @param {type} action
- * @returns {undefined}
- */
- (function($, window, document, name) {
- var CLASS_OFF_CANVAS_LEFT = $.className('off-canvas-left');
- var CLASS_OFF_CANVAS_RIGHT = $.className('off-canvas-right');
- var CLASS_ACTION_BACKDROP = $.className('off-canvas-backdrop');
- var CLASS_OFF_CANVAS_WRAP = $.className('off-canvas-wrap');
- var CLASS_SLIDE_IN = $.className('slide-in');
- var CLASS_ACTIVE = $.className('active');
- var CLASS_TRANSITIONING = $.className('transitioning');
- var SELECTOR_INNER_WRAP = $.classSelector('.inner-wrap');
- var OffCanvas = $.Class.extend({
- init: function(element, options) {
- this.wrapper = this.element = element;
- this.scroller = this.wrapper.querySelector(SELECTOR_INNER_WRAP);
- this.classList = this.wrapper.classList;
- if (this.scroller) {
- this.options = $.extend(true, {
- dragThresholdX: 10,
- scale: 0.8,
- opacity: 0.1,
- preventDefaultException: {
- tagName: /^(INPUT|TEXTAREA|BUTTON|SELECT|VIDEO)$/
- },
- }, options);
- document.body.classList.add($.className('fullscreen')); //fullscreen
- this.refresh();
- this.initEvent();
- }
- },
- _preventDefaultException: function(el, exceptions) {
- for (var i in exceptions) {
- if (exceptions[i].test(el[i])) {
- return true;
- }
- }
- return false;
- },
- refresh: function(offCanvas) {
- // offCanvas && !offCanvas.classList.contains(CLASS_ACTIVE) && this.classList.remove(CLASS_ACTIVE);
- this.slideIn = this.classList.contains(CLASS_SLIDE_IN);
- this.scalable = this.classList.contains($.className('scalable')) && !this.slideIn;
- this.scroller = this.wrapper.querySelector(SELECTOR_INNER_WRAP);
- // !offCanvas && this.scroller.classList.remove(CLASS_TRANSITIONING);
- // !offCanvas && this.scroller.setAttribute('style', '');
- this.offCanvasLefts = this.wrapper.querySelectorAll('.' + CLASS_OFF_CANVAS_LEFT);
- this.offCanvasRights = this.wrapper.querySelectorAll('.' + CLASS_OFF_CANVAS_RIGHT);
- if (offCanvas) {
- if (offCanvas.classList.contains(CLASS_OFF_CANVAS_LEFT)) {
- this.offCanvasLeft = offCanvas;
- } else if (offCanvas.classList.contains(CLASS_OFF_CANVAS_RIGHT)) {
- this.offCanvasRight = offCanvas;
- }
- } else {
- this.offCanvasRight = this.wrapper.querySelector('.' + CLASS_OFF_CANVAS_RIGHT);
- this.offCanvasLeft = this.wrapper.querySelector('.' + CLASS_OFF_CANVAS_LEFT);
- }
- this.offCanvasRightWidth = this.offCanvasLeftWidth = 0;
- this.offCanvasLeftSlideIn = this.offCanvasRightSlideIn = false;
- if (this.offCanvasRight) {
- this.offCanvasRightWidth = this.offCanvasRight.offsetWidth;
- this.offCanvasRightSlideIn = this.slideIn && (this.offCanvasRight.parentNode === this.wrapper);
- // this.offCanvasRight.classList.remove(CLASS_TRANSITIONING);
- // this.offCanvasRight.classList.remove(CLASS_ACTIVE);
- // this.offCanvasRight.setAttribute('style', '');
- }
- if (this.offCanvasLeft) {
- this.offCanvasLeftWidth = this.offCanvasLeft.offsetWidth;
- this.offCanvasLeftSlideIn = this.slideIn && (this.offCanvasLeft.parentNode === this.wrapper);
- // this.offCanvasLeft.classList.remove(CLASS_TRANSITIONING);
- // this.offCanvasLeft.classList.remove(CLASS_ACTIVE);
- // this.offCanvasLeft.setAttribute('style', '');
- }
- this.backdrop = this.scroller.querySelector('.' + CLASS_ACTION_BACKDROP);
- this.options.dragThresholdX = this.options.dragThresholdX || 10;
- this.visible = false;
- this.startX = null;
- this.lastX = null;
- this.offsetX = null;
- this.lastTranslateX = null;
- },
- handleEvent: function(e) {
- switch (e.type) {
- case $.EVENT_START:
- e.target && !this._preventDefaultException(e.target, this.options.preventDefaultException) && e.preventDefault();
- break;
- case 'webkitTransitionEnd': //有个bug需要处理,需要考虑假设没有触发webkitTransitionEnd的情况
- if (e.target === this.scroller) {
- this._dispatchEvent();
- }
- break;
- case 'drag':
- var detail = e.detail;
- if (!this.startX) {
- this.startX = detail.center.x;
- this.lastX = this.startX;
- } else {
- this.lastX = detail.center.x;
- }
- if (!this.isDragging && Math.abs(this.lastX - this.startX) > this.options.dragThresholdX && (detail.direction === 'left' || (detail.direction === 'right'))) {
- if (this.slideIn) {
- this.scroller = this.wrapper.querySelector(SELECTOR_INNER_WRAP);
- if (this.classList.contains(CLASS_ACTIVE)) {
- if (this.offCanvasRight && this.offCanvasRight.classList.contains(CLASS_ACTIVE)) {
- this.offCanvas = this.offCanvasRight;
- this.offCanvasWidth = this.offCanvasRightWidth;
- } else {
- this.offCanvas = this.offCanvasLeft;
- this.offCanvasWidth = this.offCanvasLeftWidth;
- }
- } else {
- if (detail.direction === 'left' && this.offCanvasRight) {
- this.offCanvas = this.offCanvasRight;
- this.offCanvasWidth = this.offCanvasRightWidth;
- } else if (detail.direction === 'right' && this.offCanvasLeft) {
- this.offCanvas = this.offCanvasLeft;
- this.offCanvasWidth = this.offCanvasLeftWidth;
- } else {
- this.scroller = null;
- }
- }
- } else {
- if (this.classList.contains(CLASS_ACTIVE)) {
- if (detail.direction === 'left') {
- this.offCanvas = this.offCanvasLeft;
- this.offCanvasWidth = this.offCanvasLeftWidth;
- } else {
- this.offCanvas = this.offCanvasRight;
- this.offCanvasWidth = this.offCanvasRightWidth;
- }
- } else {
- if (detail.direction === 'right') {
- this.offCanvas = this.offCanvasLeft;
- this.offCanvasWidth = this.offCanvasLeftWidth;
- } else {
- this.offCanvas = this.offCanvasRight;
- this.offCanvasWidth = this.offCanvasRightWidth;
- }
- }
- }
- if (this.offCanvas && this.scroller) {
- this.startX = this.lastX;
- this.isDragging = true;
- $.gestures.session.lockDirection = true; //锁定方向
- $.gestures.session.startDirection = detail.direction;
- this.offCanvas.classList.remove(CLASS_TRANSITIONING);
- this.scroller.classList.remove(CLASS_TRANSITIONING);
- this.offsetX = this.getTranslateX();
- this._initOffCanvasVisible();
- }
- }
- if (this.isDragging) {
- this.updateTranslate(this.offsetX + (this.lastX - this.startX));
- detail.gesture.preventDefault();
- e.stopPropagation();
- }
- break;
- case 'dragend':
- if (this.isDragging) {
- var detail = e.detail;
- var direction = detail.direction;
- this.isDragging = false;
- this.offCanvas.classList.add(CLASS_TRANSITIONING);
- this.scroller.classList.add(CLASS_TRANSITIONING);
- var ratio = 0;
- var x = this.getTranslateX();
- if (!this.slideIn) {
- if (x >= 0) {
- ratio = (this.offCanvasLeftWidth && (x / this.offCanvasLeftWidth)) || 0;
- } else {
- ratio = (this.offCanvasRightWidth && (x / this.offCanvasRightWidth)) || 0;
- }
- if (ratio === 0) {
- this.openPercentage(0);
- this._dispatchEvent(); //此处不触发webkitTransitionEnd,所以手动dispatch
- return;
- }
- if (direction === 'right' && ratio >= 0 && (ratio >= 0.5 || detail.swipe)) { //右滑打开
- this.openPercentage(100);
- } else if (direction === 'right' && ratio < 0 && (ratio > -0.5 || detail.swipe)) { //右滑关闭
- this.openPercentage(0);
- } else if (direction === 'right' && ratio > 0 && ratio < 0.5) { //右滑还原关闭
- this.openPercentage(0);
- } else if (direction === 'right' && ratio < 0.5) { //右滑还原打开
- this.openPercentage(-100);
- } else if (direction === 'left' && ratio <= 0 && (ratio <= -0.5 || detail.swipe)) { //左滑打开
- this.openPercentage(-100);
- } else if (direction === 'left' && ratio > 0 && (ratio <= 0.5 || detail.swipe)) { //左滑关闭
- this.openPercentage(0);
- } else if (direction === 'left' && ratio < 0 && ratio >= -0.5) { //左滑还原关闭
- this.openPercentage(0);
- } else if (direction === 'left' && ratio > 0.5) { //左滑还原打开
- this.openPercentage(100);
- } else { //默认关闭
- this.openPercentage(0);
- }
- if (ratio === 1 || ratio === -1) { //此处不触发webkitTransitionEnd,所以手动dispatch
- this._dispatchEvent();
- }
- } else {
- if (x >= 0) {
- ratio = (this.offCanvasRightWidth && (x / this.offCanvasRightWidth)) || 0;
- } else {
- ratio = (this.offCanvasLeftWidth && (x / this.offCanvasLeftWidth)) || 0;
- }
- if (direction === 'right' && ratio <= 0 && (ratio >= -0.5 || detail.swipe)) { //右滑打开
- this.openPercentage(100);
- } else if (direction === 'right' && ratio > 0 && (ratio >= 0.5 || detail.swipe)) { //右滑关闭
- this.openPercentage(0);
- } else if (direction === 'right' && ratio <= -0.5) { //右滑还原关闭
- this.openPercentage(0);
- } else if (direction === 'right' && ratio > 0 && ratio <= 0.5) { //右滑还原打开
- this.openPercentage(-100);
- } else if (direction === 'left' && ratio >= 0 && (ratio <= 0.5 || detail.swipe)) { //左滑打开
- this.openPercentage(-100);
- } else if (direction === 'left' && ratio < 0 && (ratio <= -0.5 || detail.swipe)) { //左滑关闭
- this.openPercentage(0);
- } else if (direction === 'left' && ratio >= 0.5) { //左滑还原关闭
- this.openPercentage(0);
- } else if (direction === 'left' && ratio >= -0.5 && ratio < 0) { //左滑还原打开
- this.openPercentage(100);
- } else {
- this.openPercentage(0);
- }
- if (ratio === 1 || ratio === -1 || ratio === 0) {
- this._dispatchEvent();
- return;
- }
- }
- }
- break;
- }
- },
- _dispatchEvent: function() {
- if (this.classList.contains(CLASS_ACTIVE)) {
- $.trigger(this.wrapper, 'shown', this);
- } else {
- $.trigger(this.wrapper, 'hidden', this);
- }
- },
- _initOffCanvasVisible: function() {
- if (!this.visible) {
- this.visible = true;
- if (this.offCanvasLeft) {
- this.offCanvasLeft.style.visibility = 'visible';
- }
- if (this.offCanvasRight) {
- this.offCanvasRight.style.visibility = 'visible';
- }
- }
- },
- initEvent: function() {
- var self = this;
- if (self.backdrop) {
- self.backdrop.addEventListener('tap', function(e) {
- self.close();
- e.detail.gesture.preventDefault();
- });
- }
- if (this.classList.contains($.className('draggable'))) {
- this.wrapper.addEventListener($.EVENT_START, this); //临时处理
- this.wrapper.addEventListener('drag', this);
- this.wrapper.addEventListener('dragend', this);
- }
- this.wrapper.addEventListener('webkitTransitionEnd', this);
- },
- openPercentage: function(percentage) {
- var p = percentage / 100;
- if (!this.slideIn) {
- if (this.offCanvasLeft && percentage >= 0) {
- this.updateTranslate(this.offCanvasLeftWidth * p);
- this.offCanvasLeft.classList[p !== 0 ? 'add' : 'remove'](CLASS_ACTIVE);
- } else if (this.offCanvasRight && percentage <= 0) {
- this.updateTranslate(this.offCanvasRightWidth * p);
- this.offCanvasRight.classList[p !== 0 ? 'add' : 'remove'](CLASS_ACTIVE);
- }
- this.classList[p !== 0 ? 'add' : 'remove'](CLASS_ACTIVE);
- } else {
- if (this.offCanvasLeft && percentage >= 0) {
- p = p === 0 ? -1 : 0;
- this.updateTranslate(this.offCanvasLeftWidth * p);
- this.offCanvasLeft.classList[percentage !== 0 ? 'add' : 'remove'](CLASS_ACTIVE);
- } else if (this.offCanvasRight && percentage <= 0) {
- p = p === 0 ? 1 : 0;
- this.updateTranslate(this.offCanvasRightWidth * p);
- this.offCanvasRight.classList[percentage !== 0 ? 'add' : 'remove'](CLASS_ACTIVE);
- }
- this.classList[percentage !== 0 ? 'add' : 'remove'](CLASS_ACTIVE);
- }
- },
- updateTranslate: function(x) {
- if (x !== this.lastTranslateX) {
- if (!this.slideIn) {
- if ((!this.offCanvasLeft && x > 0) || (!this.offCanvasRight && x < 0)) {
- this.setTranslateX(0);
- return;
- }
- if (this.leftShowing && x > this.offCanvasLeftWidth) {
- this.setTranslateX(this.offCanvasLeftWidth);
- return;
- }
- if (this.rightShowing && x < -this.offCanvasRightWidth) {
- this.setTranslateX(-this.offCanvasRightWidth);
- return;
- }
- this.setTranslateX(x);
- if (x >= 0) {
- this.leftShowing = true;
- this.rightShowing = false;
- if (x > 0) {
- if (this.offCanvasLeft) {
- $.each(this.offCanvasLefts, function(index, offCanvas) {
- if (offCanvas === this.offCanvasLeft) {
- this.offCanvasLeft.style.zIndex = 0;
- } else {
- offCanvas.style.zIndex = -1;
- }
- }.bind(this));
- }
- if (this.offCanvasRight) {
- this.offCanvasRight.style.zIndex = -1;
- }
- }
- } else {
- this.rightShowing = true;
- this.leftShowing = false;
- if (this.offCanvasRight) {
- $.each(this.offCanvasRights, function(index, offCanvas) {
- if (offCanvas === this.offCanvasRight) {
- offCanvas.style.zIndex = 0;
- } else {
- offCanvas.style.zIndex = -1;
- }
- }.bind(this));
- }
- if (this.offCanvasLeft) {
- this.offCanvasLeft.style.zIndex = -1;
- }
- }
- } else {
- if (this.offCanvas.classList.contains(CLASS_OFF_CANVAS_RIGHT)) {
- if (x < 0) {
- this.setTranslateX(0);
- return;
- }
- if (x > this.offCanvasRightWidth) {
- this.setTranslateX(this.offCanvasRightWidth);
- return;
- }
- } else {
- if (x > 0) {
- this.setTranslateX(0);
- return;
- }
- if (x < -this.offCanvasLeftWidth) {
- this.setTranslateX(-this.offCanvasLeftWidth);
- return;
- }
- }
- this.setTranslateX(x);
- }
- this.lastTranslateX = x;
- }
- },
- setTranslateX: $.animationFrame(function(x) {
- if (this.scroller) {
- if (this.scalable && this.offCanvas.parentNode === this.wrapper) {
- var percent = Math.abs(x) / this.offCanvasWidth;
- var zoomOutScale = 1 - (1 - this.options.scale) * percent;
- var zoomInScale = this.options.scale + (1 - this.options.scale) * percent;
- var zoomOutOpacity = 1 - (1 - this.options.opacity) * percent;
- var zoomInOpacity = this.options.opacity + (1 - this.options.opacity) * percent;
- if (this.offCanvas.classList.contains(CLASS_OFF_CANVAS_LEFT)) {
- this.offCanvas.style.webkitTransformOrigin = '-100%';
- this.scroller.style.webkitTransformOrigin = 'left';
- } else {
- this.offCanvas.style.webkitTransformOrigin = '200%';
- this.scroller.style.webkitTransformOrigin = 'right';
- }
- this.offCanvas.style.opacity = zoomInOpacity;
- this.offCanvas.style.webkitTransform = 'translate3d(0,0,0) scale(' + zoomInScale + ')';
- this.scroller.style.webkitTransform = 'translate3d(' + x + 'px,0,0) scale(' + zoomOutScale + ')';
- } else {
- if (this.slideIn) {
- this.offCanvas.style.webkitTransform = 'translate3d(' + x + 'px,0,0)';
- } else {
- this.scroller.style.webkitTransform = 'translate3d(' + x + 'px,0,0)';
- }
- }
- }
- }),
- getTranslateX: function() {
- if (this.offCanvas) {
- var scroller = this.slideIn ? this.offCanvas : this.scroller;
- var result = $.parseTranslateMatrix($.getStyles(scroller, 'webkitTransform'));
- return (result && result.x) || 0;
- }
- return 0;
- },
- isShown: function(direction) {
- var shown = false;
- if (!this.slideIn) {
- var x = this.getTranslateX();
- if (direction === 'right') {
- shown = this.classList.contains(CLASS_ACTIVE) && x < 0;
- } else if (direction === 'left') {
- shown = this.classList.contains(CLASS_ACTIVE) && x > 0;
- } else {
- shown = this.classList.contains(CLASS_ACTIVE) && x !== 0;
- }
- } else {
- if (direction === 'left') {
- shown = this.classList.contains(CLASS_ACTIVE) && this.wrapper.querySelector('.' + CLASS_OFF_CANVAS_LEFT + '.' + CLASS_ACTIVE);
- } else if (direction === 'right') {
- shown = this.classList.contains(CLASS_ACTIVE) && this.wrapper.querySelector('.' + CLASS_OFF_CANVAS_RIGHT + '.' + CLASS_ACTIVE);
- } else {
- shown = this.classList.contains(CLASS_ACTIVE) && (this.wrapper.querySelector('.' + CLASS_OFF_CANVAS_LEFT + '.' + CLASS_ACTIVE) || this.wrapper.querySelector('.' + CLASS_OFF_CANVAS_RIGHT + '.' + CLASS_ACTIVE));
- }
- }
- return shown;
- },
- close: function() {
- this._initOffCanvasVisible();
- this.offCanvas = this.wrapper.querySelector('.' + CLASS_OFF_CANVAS_RIGHT + '.' + CLASS_ACTIVE) || this.wrapper.querySelector('.' + CLASS_OFF_CANVAS_LEFT + '.' + CLASS_ACTIVE);
- this.offCanvasWidth = this.offCanvas.offsetWidth;
- if (this.scroller) {
- this.offCanvas.offsetHeight;
- this.offCanvas.classList.add(CLASS_TRANSITIONING);
- this.scroller.classList.add(CLASS_TRANSITIONING);
- this.openPercentage(0);
- }
- },
- show: function(direction) {
- this._initOffCanvasVisible();
- if (this.isShown(direction)) {
- return false;
- }
- if (!direction) {
- direction = this.wrapper.querySelector('.' + CLASS_OFF_CANVAS_RIGHT) ? 'right' : 'left';
- }
- if (direction === 'right') {
- this.offCanvas = this.offCanvasRight;
- this.offCanvasWidth = this.offCanvasRightWidth;
- } else {
- this.offCanvas = this.offCanvasLeft;
- this.offCanvasWidth = this.offCanvasLeftWidth;
- }
- if (this.scroller) {
- this.offCanvas.offsetHeight;
- this.offCanvas.classList.add(CLASS_TRANSITIONING);
- this.scroller.classList.add(CLASS_TRANSITIONING);
- this.openPercentage(direction === 'left' ? 100 : -100);
- }
- return true;
- },
- toggle: function(directionOrOffCanvas) {
- var direction = directionOrOffCanvas;
- if (directionOrOffCanvas && directionOrOffCanvas.classList) {
- direction = directionOrOffCanvas.classList.contains(CLASS_OFF_CANVAS_LEFT) ? 'left' : 'right';
- this.refresh(directionOrOffCanvas);
- }
- if (!this.show(direction)) {
- this.close();
- }
- }
- });
- //hash to offcanvas
- var findOffCanvasContainer = function(target) {
- parentNode = target.parentNode;
- if (parentNode) {
- if (parentNode.classList.contains(CLASS_OFF_CANVAS_WRAP)) {
- return parentNode;
- } else {
- parentNode = parentNode.parentNode;
- if (parentNode.classList.contains(CLASS_OFF_CANVAS_WRAP)) {
- return parentNode;
- }
- }
- }
- };
- var handle = function(event, target) {
- if (target.tagName === 'A' && target.hash) {
- var offcanvas = document.getElementById(target.hash.replace('#', ''));
- if (offcanvas) {
- var container = findOffCanvasContainer(offcanvas);
- if (container) {
- $.targets._container = container;
- return offcanvas;
- }
- }
- }
- return false;
- };
- $.registerTarget({
- name: name,
- index: 60,
- handle: handle,
- target: false,
- isReset: false,
- isContinue: true
- });
- window.addEventListener('tap', function(e) {
- if (!$.targets.offcanvas) {
- return;
- }
- //TODO 此处类型的代码后续考虑统一优化(target机制),现在的实现费力不讨好
- var target = e.target;
- for (; target && target !== document; target = target.parentNode) {
- if (target.tagName === 'A' && target.hash && target.hash === ('#' + $.targets.offcanvas.id)) {
- e.detail && e.detail.gesture && e.detail.gesture.preventDefault(); //fixed hashchange
- $($.targets._container).offCanvas().toggle($.targets.offcanvas);
- $.targets.offcanvas = $.targets._container = null;
- break;
- }
- }
- });
- $.fn.offCanvas = function(options) {
- var offCanvasApis = [];
- this.each(function() {
- var offCanvasApi = null;
- var self = this;
- //hack old version
- if (!self.classList.contains(CLASS_OFF_CANVAS_WRAP)) {
- self = findOffCanvasContainer(self);
- }
- var id = self.getAttribute('data-offCanvas');
- if (!id) {
- id = ++$.uuid;
- $.data[id] = offCanvasApi = new OffCanvas(self, options);
- self.setAttribute('data-offCanvas', id);
- } else {
- offCanvasApi = $.data[id];
- }
- if (options === 'show' || options === 'close' || options === 'toggle') {
- offCanvasApi.toggle();
- }
- offCanvasApis.push(offCanvasApi);
- });
- return offCanvasApis.length === 1 ? offCanvasApis[0] : offCanvasApis;
- };
- $.ready(function() {
- $($.classSelector('.off-canvas-wrap')).offCanvas();
- });
- })(mui, window, document, 'offcanvas');
|