mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-20 04:14:21 +08:00
SlideGesture
This commit is contained in:
@ -31,7 +31,6 @@
|
|||||||
"jasmine-core": "^2.2.0",
|
"jasmine-core": "^2.2.0",
|
||||||
"karma-chrome-launcher": "^0.1.7",
|
"karma-chrome-launcher": "^0.1.7",
|
||||||
"karma-jasmine": "^0.3.5",
|
"karma-jasmine": "^0.3.5",
|
||||||
"standard": "driftyco/standard#master",
|
|
||||||
"systemjs": "~0.11.0",
|
"systemjs": "~0.11.0",
|
||||||
"through2": "^0.6.3",
|
"through2": "^0.6.3",
|
||||||
"traceur": "0.0.87",
|
"traceur": "0.0.87",
|
||||||
|
@ -1,40 +1,32 @@
|
|||||||
<ion-aside-parent>
|
<ion-aside-parent>
|
||||||
<ion-aside side="left">
|
<ion-aside side="left">
|
||||||
Hello ...
|
LEFT
|
||||||
<p>...</p>
|
<p>...</p>
|
||||||
<p>...</p>
|
<p>...</p>
|
||||||
|
<p>...</p>
|
||||||
|
Side menu!
|
||||||
|
</ion-aside>
|
||||||
|
<ion-aside side="right">
|
||||||
|
RIGHT
|
||||||
|
<p>...</p>
|
||||||
|
<p>...</p>
|
||||||
|
<p>...</p>
|
||||||
|
Side menu!
|
||||||
|
</ion-aside>
|
||||||
|
<ion-aside side="top">
|
||||||
|
TOP
|
||||||
|
<p>...</p>
|
||||||
|
<p>...</p>
|
||||||
|
<p>...</p>
|
||||||
|
Side menu!
|
||||||
|
</ion-aside>
|
||||||
|
<ion-aside side="bottom">
|
||||||
|
BOTTOM
|
||||||
<p>...</p>
|
<p>...</p>
|
||||||
<p>...</p>
|
<p>...</p>
|
||||||
<p>...</p>
|
<p>...</p>
|
||||||
Side menu!
|
Side menu!
|
||||||
</ion-aside>
|
</ion-aside>
|
||||||
<!-- <ion-aside side="bottom"> -->
|
|
||||||
<!-- Hello ... -->
|
|
||||||
<!-- <p>...</p> -->
|
|
||||||
<!-- <p>...</p> -->
|
|
||||||
<!-- <p>...</p> -->
|
|
||||||
<!-- <p>...</p> -->
|
|
||||||
<!-- <p>...</p> -->
|
|
||||||
<!-- Side menu! -->
|
|
||||||
<!-- </ion-aside> -->
|
|
||||||
<!-- <ion-aside side="top"> -->
|
|
||||||
<!-- Hello ... -->
|
|
||||||
<!-- <p>...</p> -->
|
|
||||||
<!-- <p>...</p> -->
|
|
||||||
<!-- <p>...</p> -->
|
|
||||||
<!-- <p>...</p> -->
|
|
||||||
<!-- <p>...</p> -->
|
|
||||||
<!-- Side menu! -->
|
|
||||||
<!-- </ion-aside> -->
|
|
||||||
<!-- <ion-aside side="left"> -->
|
|
||||||
<!-- Hello ... -->
|
|
||||||
<!-- <p>...</p> -->
|
|
||||||
<!-- <p>...</p> -->
|
|
||||||
<!-- <p>...</p> -->
|
|
||||||
<!-- <p>...</p> -->
|
|
||||||
<!-- <p>...</p> -->
|
|
||||||
<!-- Side menu! -->
|
|
||||||
<!-- </ion-aside> -->
|
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<button (click)="showModal()">Show Modal</button>
|
<button (click)="showModal()">Show Modal</button>
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import {Component, Template, Inject, Parent, NgElement} from 'angular2/angular2';
|
import {Component, Template, Inject, Parent, NgElement} from 'angular2/angular2';
|
||||||
import {Ion} from '../ion';
|
import {Ion} from '../ion';
|
||||||
import {IonConfig} from '../../config';
|
import {IonConfig} from '../../config';
|
||||||
import {DragGesture} from '../../core/gestures/drag-gesture';
|
import {SlideEdgeGesture} from '../../core/gestures/slide-edge-gesture';
|
||||||
import * as util from '../../util';
|
import * as util from '../../util';
|
||||||
|
|
||||||
export var asideConfig = new IonConfig('aside');
|
export var asideConfig = new IonConfig('aside');
|
||||||
@ -31,25 +31,24 @@ export class Aside {
|
|||||||
|
|
||||||
this._drag = {};
|
this._drag = {};
|
||||||
|
|
||||||
this.gesture = new DragGesture(asideParent.domElement, {
|
|
||||||
onDrag: this.onDrag.bind(this),
|
|
||||||
onDragStart: this.onDragStart.bind(this),
|
|
||||||
onDragEnd: this.onDragEnd.bind(this)
|
|
||||||
});
|
|
||||||
this.dragMethods = {
|
|
||||||
getMenuStart() { return 0; },
|
|
||||||
getEventPos(ev) { return ev.center.x; },
|
|
||||||
canStart() { return true; }
|
|
||||||
};
|
|
||||||
this.gesture.listen();
|
|
||||||
|
|
||||||
this.domElement.addEventListener('transitionend', ev => {
|
this.domElement.addEventListener('transitionend', ev => {
|
||||||
this.setChanging(false);
|
this.setChanging(false);
|
||||||
})
|
})
|
||||||
|
|
||||||
|
let gestureConstructor = {
|
||||||
|
left: LeftAsideSlideGesture,
|
||||||
|
top: TopAsideSlideGesture,
|
||||||
|
bottom: BottomAsideSlideGesture,
|
||||||
|
right: RightAsideSlideGesture
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
// TODO: remove this. setTimeout has to be done so the bindings can be applied
|
// TODO: remove this. setTimeout has to be done so the bindings can be applied
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
asideConfig.invoke(this);
|
// asideConfig.invoke(this);
|
||||||
|
this.domElement.classList.add(this.side);
|
||||||
|
this.gesture = new gestureConstructor[this.side](this, asideParent.domElement);
|
||||||
|
this.gesture.listen();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
onDragStart(ev) {
|
onDragStart(ev) {
|
||||||
@ -83,6 +82,9 @@ export class Aside {
|
|||||||
this.dragMethods.onEnd(this._drag, ev);
|
this.dragMethods.onEnd(this._drag, ev);
|
||||||
this._drag = null;
|
this._drag = null;
|
||||||
}
|
}
|
||||||
|
setSliding(isSliding) {
|
||||||
|
this.domElement.classList[isSliding ? 'add' : 'remove']('sliding');
|
||||||
|
}
|
||||||
setChanging(isChanging) {
|
setChanging(isChanging) {
|
||||||
if (isChanging !== this.isChanging) {
|
if (isChanging !== this.isChanging) {
|
||||||
this.isChanging = isChanging;
|
this.isChanging = isChanging;
|
||||||
@ -112,3 +114,87 @@ export class AsideParent {
|
|||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class AsideSlideGesture extends SlideEdgeGesture {
|
||||||
|
constructor(aside: Aside, slideElement: Element) {
|
||||||
|
this.aside = aside;
|
||||||
|
super(slideElement, {
|
||||||
|
direction: (aside.side === 'left' || aside.side === 'right') ? 'x' : 'y',
|
||||||
|
edge: aside.side || 'left',
|
||||||
|
threshold: aside.dragThreshold || 100
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
canStart(ev) {
|
||||||
|
// Only restrict edges if the aside is closed
|
||||||
|
return this.aside.isOpen ? true : super.canStart(ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
onSlideBeforeStart(slide, ev) {
|
||||||
|
this.aside.setSliding(true);
|
||||||
|
this.aside.setChanging(true);
|
||||||
|
return new Promise(resolve => {
|
||||||
|
requestAnimationFrame(resolve);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
onSlide(slide, ev) {
|
||||||
|
this.aside.domElement.style.transform = 'translate3d(' + slide.distance + 'px,0,0)';
|
||||||
|
}
|
||||||
|
onSlideEnd(slide, ev) {
|
||||||
|
this.aside.domElement.style.transform = '';
|
||||||
|
this.aside.setSliding(false);
|
||||||
|
if (Math.abs(ev.velocityX) > 0.2 || Math.abs(slide.delta) > Math.abs(slide.max) * 0.5) {
|
||||||
|
this.aside.setOpen(!this.aside.isOpen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getElementStartPos(slide, ev) {
|
||||||
|
return this.aside.isOpen ? slide.max : slide.min;
|
||||||
|
}
|
||||||
|
getSlideBoundaries() {
|
||||||
|
return {
|
||||||
|
min: 0,
|
||||||
|
max: this.aside.domElement.offsetWidth
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LeftAsideSlideGesture extends AsideSlideGesture {}
|
||||||
|
|
||||||
|
class RightAsideSlideGesture extends LeftAsideSlideGesture {
|
||||||
|
getElementStartPos(slide, ev) {
|
||||||
|
return this.aside.isOpen ? slide.min : slide.max;
|
||||||
|
}
|
||||||
|
getSlideBoundaries() {
|
||||||
|
return {
|
||||||
|
min: -this.aside.domElement.offsetWidth,
|
||||||
|
max: 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
import Hammer from 'hammer';
|
||||||
|
class TopAsideSlideGesture extends AsideSlideGesture {
|
||||||
|
onSlide(slide, ev) {
|
||||||
|
this.aside.domElement.style.transform = 'translate3d(0,' + slide.distance + 'px,0)';
|
||||||
|
}
|
||||||
|
getSlideBoundaries() {
|
||||||
|
return {
|
||||||
|
min: 0,
|
||||||
|
max: this.aside.domElement.offsetHeight
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BottomAsideSlideGesture extends TopAsideSlideGesture {
|
||||||
|
getElementStartPos(slide, ev) {
|
||||||
|
return this.aside.isOpen ? slide.min : slide.max;
|
||||||
|
}
|
||||||
|
getSlideBoundaries() {
|
||||||
|
return {
|
||||||
|
min: -this.aside.domElement.offsetHeight,
|
||||||
|
max: 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -12,52 +12,52 @@ ion-aside {
|
|||||||
&:not(.open):not(.changing) {
|
&:not(.open):not(.changing) {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
&.dragging {
|
&.sliding {
|
||||||
transition-duration: 0s;
|
transition-duration: 0s;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.left {
|
&.left {
|
||||||
width: $aside-width;
|
width: $aside-width;
|
||||||
left: 0;
|
left: -$aside-width;
|
||||||
top: 0;
|
top: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
|
|
||||||
transform: translate3d(-$aside-width, 0, 0);
|
transform: translate3d(0, 0, 0);
|
||||||
&.open {
|
&.open {
|
||||||
transform: translate3d(0,0,0);
|
transform: translate3d($aside-width,0,0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&.right {
|
&.right {
|
||||||
width: $aside-width;
|
width: $aside-width;
|
||||||
right: 0;
|
left: 100%;
|
||||||
top: 0;
|
top: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
|
|
||||||
transform: translate3d($aside-width,0,0);
|
|
||||||
&.open {
|
|
||||||
transform: translate3d(0,0,0);
|
transform: translate3d(0,0,0);
|
||||||
|
&.open {
|
||||||
|
transform: translate3d(-$aside-width,0,0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&.top {
|
&.top {
|
||||||
height: $aside-width;
|
height: $aside-width;
|
||||||
top: 0;
|
top: -$aside-width;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
|
|
||||||
transform: translate3d(0,-$aside-width,0);
|
|
||||||
&.open {
|
|
||||||
transform: translate3d(0,0,0);
|
transform: translate3d(0,0,0);
|
||||||
|
&.open {
|
||||||
|
transform: translate3d(0,$aside-width,0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&.bottom {
|
&.bottom {
|
||||||
height: $aside-width;
|
height: $aside-width;
|
||||||
bottom: 0;
|
top: 100%;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
|
|
||||||
transform: translate3d(0,$aside-width,0);
|
|
||||||
&.open {
|
|
||||||
transform: translate3d(0,0,0);
|
transform: translate3d(0,0,0);
|
||||||
|
&.open {
|
||||||
|
transform: translate3d(0,-$aside-width,0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ import {asideConfig} from '../../aside';
|
|||||||
import Hammer from 'hammer';
|
import Hammer from 'hammer';
|
||||||
import * as util from '../../../../util';
|
import * as util from '../../../../util';
|
||||||
|
|
||||||
|
/*
|
||||||
asideConfig
|
asideConfig
|
||||||
.behavior(function() {
|
.behavior(function() {
|
||||||
if (this.side !== 'bottom') return;
|
if (this.side !== 'bottom') return;
|
||||||
@ -116,3 +117,4 @@ asideConfig
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
*/
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import * as Platform from '../platform';
|
import * as Platform from '../platform';
|
||||||
import * as util from '../util';
|
import * as util from '../util';
|
||||||
|
|
||||||
var ILLEGAL_ASSIGN_FIELDS = {};
|
|
||||||
export class Ion {
|
export class Ion {
|
||||||
|
|
||||||
extend(...args) {
|
extend(...args) {
|
||||||
|
@ -1,32 +1,36 @@
|
|||||||
import {Gesture} from './gesture';
|
import {Gesture} from './gesture';
|
||||||
import * as util from '../../util';
|
import * as util from '../../util';
|
||||||
|
import Hammer from 'hammer';
|
||||||
|
|
||||||
|
/*
|
||||||
|
* BUG(ajoslin): HammerJS 2.x does not have an alternative to HammerJS 1.x's
|
||||||
|
* dragLockToAxis, so a vertical and horizontal gesture can happen at the same time.
|
||||||
|
*/
|
||||||
export class DragGesture extends Gesture {
|
export class DragGesture extends Gesture {
|
||||||
constructor(element, opts = {}) {
|
constructor(element, opts = {}) {
|
||||||
util.extend(this, {
|
util.defaults(opts, {});
|
||||||
onDrag: opts.onDrag,
|
|
||||||
onDragEnd: opts.onDragEnd,
|
|
||||||
onDragStart: opts.onDragStart
|
|
||||||
});
|
|
||||||
super(element, opts);
|
super(element, opts);
|
||||||
}
|
}
|
||||||
listen() {
|
listen() {
|
||||||
super.listen();
|
super.listen();
|
||||||
this.hammertime.on('panstart', ev => {
|
this.hammertime.on('panstart', ev => {
|
||||||
if (this.onDragStart && this.onDragStart(ev) !== false) {
|
if (this.onDragStart(ev) !== false) {
|
||||||
this.dragging = true;
|
this.dragging = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.hammertime.on('panmove', ev => {
|
this.hammertime.on('panmove', ev => {
|
||||||
if (!this.dragging) return;
|
if (!this.dragging) return;
|
||||||
if (this.onDrag && this.onDrag(ev) === false) {
|
if (this.onDrag(ev) === false) {
|
||||||
this.dragging = false;
|
this.dragging = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.hammertime.on('panend', ev => {
|
this.hammertime.on('panend', ev => {
|
||||||
if (!this.dragging) return;
|
if (!this.dragging) return;
|
||||||
this.onDragEnd && this.onDragEnd(ev);
|
this.onDragEnd(ev);
|
||||||
this.dragging = false;
|
this.dragging = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
onDrag() {}
|
||||||
|
onDragStart() {}
|
||||||
|
onDragEnd() {}
|
||||||
}
|
}
|
||||||
|
@ -3,10 +3,16 @@ import Hammer from 'hammer';
|
|||||||
|
|
||||||
export class Gesture {
|
export class Gesture {
|
||||||
constructor(element, opts = {}) {
|
constructor(element, opts = {}) {
|
||||||
util.defaults(opts, {
|
|
||||||
});
|
|
||||||
this.element = element;
|
this.element = element;
|
||||||
|
|
||||||
|
// Map 'x' or 'y' string to hammerjs opts
|
||||||
|
this.direction = opts.direction || 'x';
|
||||||
|
opts.direction = this.direction === 'x' ?
|
||||||
|
Hammer.DIRECTION_HORIZONTAL :
|
||||||
|
Hammer.DIRECTION_VERTICAL;
|
||||||
|
|
||||||
this._options = opts;
|
this._options = opts;
|
||||||
|
|
||||||
}
|
}
|
||||||
options(opts = {}) {
|
options(opts = {}) {
|
||||||
util.extend(this._options, opts);
|
util.extend(this._options, opts);
|
||||||
@ -21,5 +27,6 @@ export class Gesture {
|
|||||||
}
|
}
|
||||||
destroy() {
|
destroy() {
|
||||||
this.hammertime.destroy();
|
this.hammertime.destroy();
|
||||||
|
this.hammertime = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
40
src/core/gestures/slide-edge-gesture.js
Normal file
40
src/core/gestures/slide-edge-gesture.js
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import {SlideGesture} from './slide-gesture';
|
||||||
|
import * as util from '../../util';
|
||||||
|
|
||||||
|
export class SlideEdgeGesture extends SlideGesture {
|
||||||
|
constructor(element: Element, opts: Object = {}) {
|
||||||
|
util.defaults(opts, {
|
||||||
|
edge: 'left',
|
||||||
|
threshold: 50
|
||||||
|
});
|
||||||
|
// Can check corners through use of eg 'left top'
|
||||||
|
this.edges = opts.edge.split(' ');
|
||||||
|
this.threshold = opts.threshold;
|
||||||
|
super(element, opts);
|
||||||
|
}
|
||||||
|
|
||||||
|
canStart(ev) {
|
||||||
|
this._containerRect = this.getContainerDimensions();
|
||||||
|
return this.edges.every(edge => this._checkEdge(edge, ev.center));
|
||||||
|
}
|
||||||
|
|
||||||
|
getContainerDimensions() {
|
||||||
|
return {
|
||||||
|
left: 0,
|
||||||
|
top: 0,
|
||||||
|
width: window.innerWidth,
|
||||||
|
height: window.innerHeight
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
_checkEdge(edge, pos) {
|
||||||
|
if ((edge === 'left' && pos.x > this._containerRect.left + this.threshold) ||
|
||||||
|
(edge === 'right' && pos.x < this._containerRect.width - this.threshold) ||
|
||||||
|
(edge === 'top' && pos.y > this._containerRect.top + this.threshold) ||
|
||||||
|
(edge === 'bottom' && pos.y < this._containerRect.height - this.threshold)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
71
src/core/gestures/slide-gesture.js
Normal file
71
src/core/gestures/slide-gesture.js
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
import {DragGesture} from './drag-gesture';
|
||||||
|
import * as util from '../../util';
|
||||||
|
|
||||||
|
export class SlideGesture extends DragGesture {
|
||||||
|
constructor(element, opts = {}) {
|
||||||
|
this.element = element;
|
||||||
|
super(element, opts);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the min and max for the slide. pageX/pageY.
|
||||||
|
* Only called on dragstart.
|
||||||
|
*/
|
||||||
|
getSlideBoundaries(slide, ev) {
|
||||||
|
return {
|
||||||
|
min: 0,
|
||||||
|
max: this.element.offsetWidth
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the element's pos when the drag starts.
|
||||||
|
* For example, an open side menu starts at 100% and a closed
|
||||||
|
* sidemenu starts at 0%.
|
||||||
|
*/
|
||||||
|
getElementStartPos(slide, ev) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
canStart() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
onDragStart(ev) {
|
||||||
|
if (!this.canStart(ev)) return false;
|
||||||
|
this.slide = {};
|
||||||
|
var promise = this.onSlideBeforeStart(this.slide, ev) || Promise.resolve();
|
||||||
|
promise.then(() => {
|
||||||
|
var {min, max} = this.getSlideBoundaries(this.slide, ev);
|
||||||
|
this.slide.min = min;
|
||||||
|
this.slide.max = max;
|
||||||
|
this.slide.elementStartPos = this.getElementStartPos(this.slide, ev);
|
||||||
|
this.slide.pointerStartPos = ev.center[this.direction];
|
||||||
|
this.slide.started = true;
|
||||||
|
this.onSlideStart(this.slide, ev);
|
||||||
|
}).catch(() => {
|
||||||
|
this.slide = null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
onDrag(ev) {
|
||||||
|
if (!this.slide || !this.slide.started) return;
|
||||||
|
this.slide.pos = ev.center[this.direction];
|
||||||
|
this.slide.distance = util.clamp(
|
||||||
|
this.slide.min,
|
||||||
|
this.slide.pos - this.slide.pointerStartPos + this.slide.elementStartPos,
|
||||||
|
this.slide.max
|
||||||
|
);
|
||||||
|
this.slide.delta = this.slide.pos - this.slide.pointerStartPos;
|
||||||
|
this.onSlide(this.slide, ev);
|
||||||
|
}
|
||||||
|
onDragEnd(ev) {
|
||||||
|
if (!this.slide || !this.slide.started) return;
|
||||||
|
this.onSlideEnd(this.slide, ev);
|
||||||
|
this.slide = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
onSlideBeforeStart() {}
|
||||||
|
onSlideStart() {}
|
||||||
|
onSlide() {}
|
||||||
|
onSlideEnd() {}
|
||||||
|
}
|
Reference in New Issue
Block a user