mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-22 21:48:42 +08:00
@ -22,6 +22,16 @@
|
|||||||
</ion-item-options>
|
</ion-item-options>
|
||||||
</ion-item-sliding>
|
</ion-item-sliding>
|
||||||
|
|
||||||
|
<ion-item>
|
||||||
|
<ion-label>Apple</ion-label>
|
||||||
|
<ion-toggle item-left></ion-toggle>
|
||||||
|
</ion-item>
|
||||||
|
|
||||||
|
<ion-item>
|
||||||
|
<ion-label>Apple</ion-label>
|
||||||
|
<ion-toggle></ion-toggle>
|
||||||
|
</ion-item>
|
||||||
|
|
||||||
<button ion-item menuClose="left" class="e2eCloseLeftMenu" detail-none>
|
<button ion-item menuClose="left" class="e2eCloseLeftMenu" detail-none>
|
||||||
Close Menu
|
Close Menu
|
||||||
</button>
|
</button>
|
||||||
@ -94,6 +104,16 @@
|
|||||||
|
|
||||||
<ion-list>
|
<ion-list>
|
||||||
|
|
||||||
|
<ion-item>
|
||||||
|
<ion-label>Apple</ion-label>
|
||||||
|
<ion-toggle item-left></ion-toggle>
|
||||||
|
</ion-item>
|
||||||
|
|
||||||
|
<ion-item>
|
||||||
|
<ion-label>Apple</ion-label>
|
||||||
|
<ion-toggle></ion-toggle>
|
||||||
|
</ion-item>
|
||||||
|
|
||||||
<button ion-item *ngFor="let p of pages" (click)="openPage(p)">
|
<button ion-item *ngFor="let p of pages" (click)="openPage(p)">
|
||||||
{{p.title}}
|
{{p.title}}
|
||||||
</button>
|
</button>
|
||||||
|
@ -74,6 +74,16 @@
|
|||||||
</ion-item-options>
|
</ion-item-options>
|
||||||
</ion-item-sliding>
|
</ion-item-sliding>
|
||||||
|
|
||||||
|
<ion-item>
|
||||||
|
<ion-label>Apple</ion-label>
|
||||||
|
<ion-toggle item-left></ion-toggle>
|
||||||
|
</ion-item>
|
||||||
|
|
||||||
|
<ion-item>
|
||||||
|
<ion-label>Apple</ion-label>
|
||||||
|
<ion-toggle></ion-toggle>
|
||||||
|
</ion-item>
|
||||||
|
|
||||||
<button ion-item (click)="goToPage1()">Push same page</button>
|
<button ion-item (click)="goToPage1()">Push same page</button>
|
||||||
|
|
||||||
<ion-item>
|
<ion-item>
|
||||||
|
45
src/components/toggle/toggle-gesture.ts
Normal file
45
src/components/toggle/toggle-gesture.ts
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import { GestureController, GesturePriority, GESTURE_TOGGLE } from '../../gestures/gesture-controller';
|
||||||
|
import { PanGesture } from '../../gestures/drag-gesture';
|
||||||
|
import { pointerCoord } from '../../util/dom';
|
||||||
|
import { NativeRafDebouncer } from '../../util/debouncer';
|
||||||
|
import { Toggle } from './toggle';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
export class ToggleGesture extends PanGesture {
|
||||||
|
|
||||||
|
constructor(public toogle: Toggle, gestureCtrl: GestureController) {
|
||||||
|
super(toogle.getNativeElement(), {
|
||||||
|
maxAngle: 20,
|
||||||
|
threshold: 0,
|
||||||
|
debouncer: new NativeRafDebouncer(),
|
||||||
|
gesture: gestureCtrl.createGesture({
|
||||||
|
name: GESTURE_TOGGLE,
|
||||||
|
priority: GesturePriority.Toggle,
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
canStart(ev: any): boolean {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
onDragStart(ev: any) {
|
||||||
|
ev.preventDefault();
|
||||||
|
|
||||||
|
this.toogle._onDragStart(pointerCoord(ev).x);
|
||||||
|
}
|
||||||
|
|
||||||
|
onDragMove(ev: any) {
|
||||||
|
ev.preventDefault();
|
||||||
|
|
||||||
|
this.toogle._onDragMove(pointerCoord(ev).x);
|
||||||
|
}
|
||||||
|
|
||||||
|
onDragEnd(ev: any) {
|
||||||
|
ev.preventDefault();
|
||||||
|
|
||||||
|
this.toogle._onDragEnd(pointerCoord(ev).x);
|
||||||
|
}
|
||||||
|
}
|
@ -3,13 +3,13 @@ import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
|
|||||||
|
|
||||||
import { Config } from '../../config/config';
|
import { Config } from '../../config/config';
|
||||||
import { Form, IonicTapInput } from '../../util/form';
|
import { Form, IonicTapInput } from '../../util/form';
|
||||||
import { isTrueProperty } from '../../util/util';
|
import { isTrueProperty, assert } from '../../util/util';
|
||||||
import { Ion } from '../ion';
|
import { Ion } from '../ion';
|
||||||
import { Item } from '../item/item';
|
import { Item } from '../item/item';
|
||||||
import { pointerCoord } from '../../util/dom';
|
|
||||||
import { Key } from '../../util/key';
|
import { Key } from '../../util/key';
|
||||||
import { Haptic } from '../../util/haptic';
|
import { Haptic } from '../../util/haptic';
|
||||||
import { UIEventManager } from '../../util/ui-event-manager';
|
import { ToggleGesture } from './toggle-gesture';
|
||||||
|
import { GestureController } from '../../gestures/gesture-controller';
|
||||||
|
|
||||||
export const TOGGLE_VALUE_ACCESSOR: any = {
|
export const TOGGLE_VALUE_ACCESSOR: any = {
|
||||||
provide: NG_VALUE_ACCESSOR,
|
provide: NG_VALUE_ACCESSOR,
|
||||||
@ -76,28 +76,18 @@ export const TOGGLE_VALUE_ACCESSOR: any = {
|
|||||||
encapsulation: ViewEncapsulation.None,
|
encapsulation: ViewEncapsulation.None,
|
||||||
})
|
})
|
||||||
export class Toggle extends Ion implements IonicTapInput, AfterContentInit, ControlValueAccessor, OnDestroy {
|
export class Toggle extends Ion implements IonicTapInput, AfterContentInit, ControlValueAccessor, OnDestroy {
|
||||||
/** @private */
|
|
||||||
_checked: boolean = false;
|
|
||||||
/** @private */
|
|
||||||
_init: boolean = false;
|
|
||||||
/** @private */
|
|
||||||
_disabled: boolean = false;
|
|
||||||
/** @private */
|
|
||||||
_labelId: string;
|
|
||||||
/** @private */
|
|
||||||
_activated: boolean = false;
|
|
||||||
/** @private */
|
|
||||||
_startX: number;
|
|
||||||
/** @private */
|
|
||||||
_msPrv: number = 0;
|
|
||||||
/** @private */
|
|
||||||
_fn: Function = null;
|
|
||||||
/** @private */
|
|
||||||
_events: UIEventManager = new UIEventManager();
|
|
||||||
|
|
||||||
/**
|
_checked: boolean = false;
|
||||||
* @private
|
_init: boolean = false;
|
||||||
*/
|
_disabled: boolean = false;
|
||||||
|
_labelId: string;
|
||||||
|
_activated: boolean = false;
|
||||||
|
_startX: number;
|
||||||
|
_msPrv: number = 0;
|
||||||
|
_fn: Function = null;
|
||||||
|
_gesture: ToggleGesture;
|
||||||
|
|
||||||
|
/** @private */
|
||||||
id: string;
|
id: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -126,8 +116,9 @@ export class Toggle extends Ion implements IonicTapInput, AfterContentInit, Cont
|
|||||||
config: Config,
|
config: Config,
|
||||||
elementRef: ElementRef,
|
elementRef: ElementRef,
|
||||||
renderer: Renderer,
|
renderer: Renderer,
|
||||||
public _haptic: Haptic,
|
private _haptic: Haptic,
|
||||||
@Optional() public _item: Item
|
@Optional() public _item: Item,
|
||||||
|
private _gestureCtrl: GestureController
|
||||||
) {
|
) {
|
||||||
super(config, elementRef, renderer, 'toggle');
|
super(config, elementRef, renderer, 'toggle');
|
||||||
_form.register(this);
|
_form.register(this);
|
||||||
@ -142,19 +133,27 @@ export class Toggle extends Ion implements IonicTapInput, AfterContentInit, Cont
|
|||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
pointerDown(ev: UIEvent): boolean {
|
ngAfterContentInit() {
|
||||||
this._startX = pointerCoord(ev).x;
|
this._init = true;
|
||||||
this._activated = true;
|
this._gesture = new ToggleGesture(this, this._gestureCtrl);
|
||||||
return true;
|
this._gesture.listen();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
pointerMove(ev: UIEvent) {
|
_onDragStart(startX: number) {
|
||||||
if (this._startX) {
|
this._startX = startX;
|
||||||
let currentX = pointerCoord(ev).x;
|
this._activated = true;
|
||||||
console.debug('toggle, pointerMove', ev.type, currentX);
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_onDragMove(currentX: number) {
|
||||||
|
assert(this._startX, '_startX must be valid');
|
||||||
|
|
||||||
|
console.debug('toggle, pointerMove', currentX);
|
||||||
|
|
||||||
if (this._checked) {
|
if (this._checked) {
|
||||||
if (currentX + 15 < this._startX) {
|
if (currentX + 15 < this._startX) {
|
||||||
@ -166,20 +165,17 @@ export class Toggle extends Ion implements IonicTapInput, AfterContentInit, Cont
|
|||||||
|
|
||||||
} else if (currentX - 15 > this._startX) {
|
} else if (currentX - 15 > this._startX) {
|
||||||
this.onChange(true);
|
this.onChange(true);
|
||||||
// Create a haptic event
|
|
||||||
this._haptic.selection();
|
this._haptic.selection();
|
||||||
this._startX = currentX;
|
this._startX = currentX;
|
||||||
this._activated = (currentX < this._startX + 5);
|
this._activated = (currentX < this._startX + 5);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
pointerUp(ev: UIEvent) {
|
_onDragEnd(endX: number) {
|
||||||
if (this._startX) {
|
assert(this._startX, '_startX must be valid');
|
||||||
let endX = pointerCoord(ev).x;
|
|
||||||
|
|
||||||
if (this.checked) {
|
if (this.checked) {
|
||||||
if (this._startX + 4 > endX) {
|
if (this._startX + 4 > endX) {
|
||||||
@ -195,7 +191,6 @@ export class Toggle extends Ion implements IonicTapInput, AfterContentInit, Cont
|
|||||||
this._activated = false;
|
this._activated = false;
|
||||||
this._startX = null;
|
this._startX = null;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @input {boolean} whether the toggle it toggled or not
|
* @input {boolean} whether the toggle it toggled or not
|
||||||
@ -273,19 +268,6 @@ export class Toggle extends Ion implements IonicTapInput, AfterContentInit, Cont
|
|||||||
*/
|
*/
|
||||||
onTouched() {}
|
onTouched() {}
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
ngAfterContentInit() {
|
|
||||||
this._init = true;
|
|
||||||
this._events.pointerEvents({
|
|
||||||
elementRef: this._elementRef,
|
|
||||||
pointerDown: this.pointerDown.bind(this),
|
|
||||||
pointerMove: this.pointerMove.bind(this),
|
|
||||||
pointerUp: this.pointerUp.bind(this)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
@ -310,7 +292,7 @@ export class Toggle extends Ion implements IonicTapInput, AfterContentInit, Cont
|
|||||||
*/
|
*/
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
this._form.deregister(this);
|
this._form.deregister(this);
|
||||||
this._events.unlistenAll();
|
this._gesture.destroy();
|
||||||
this._fn = null;
|
this._fn = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@ export interface PanGestureConfig {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
export class PanGesture {
|
export class PanGesture {
|
||||||
|
|
||||||
private debouncer: Debouncer;
|
private debouncer: Debouncer;
|
||||||
private events: UIEventManager = new UIEventManager(false);
|
private events: UIEventManager = new UIEventManager(false);
|
||||||
private pointerEvents: PointerEvents;
|
private pointerEvents: PointerEvents;
|
||||||
@ -58,8 +59,10 @@ export class PanGesture {
|
|||||||
capture: opts.capture,
|
capture: opts.capture,
|
||||||
passive: opts.passive
|
passive: opts.passive
|
||||||
};
|
};
|
||||||
|
if (opts.threshold > 0) {
|
||||||
this.detector = new PanRecognizer(opts.direction, opts.threshold, opts.maxAngle);
|
this.detector = new PanRecognizer(opts.direction, opts.threshold, opts.maxAngle);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
listen() {
|
listen() {
|
||||||
if (this.isListening) {
|
if (this.isListening) {
|
||||||
@ -100,40 +103,42 @@ export class PanGesture {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let coord = pointerCoord(ev);
|
|
||||||
this.detector.start(coord);
|
|
||||||
this.started = true;
|
this.started = true;
|
||||||
this.captured = false;
|
this.captured = false;
|
||||||
|
|
||||||
|
const coord = pointerCoord(ev);
|
||||||
|
if (this.detector) {
|
||||||
|
this.detector.start(coord);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (!this.tryToCapture(ev)) {
|
||||||
|
this.started = false;
|
||||||
|
this.captured = false;
|
||||||
|
this.gestute.release();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
pointerMove(ev: any) {
|
pointerMove(ev: any) {
|
||||||
if (!this.started) {
|
assert(this.started === true, 'started must be true');
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.debouncer.debounce(() => {
|
|
||||||
if (this.captured) {
|
if (this.captured) {
|
||||||
|
this.debouncer.debounce(() => {
|
||||||
this.onDragMove(ev);
|
this.onDragMove(ev);
|
||||||
return;
|
|
||||||
}
|
|
||||||
let coord = pointerCoord(ev);
|
|
||||||
if (this.detector.detect(coord)) {
|
|
||||||
|
|
||||||
if (this.detector.pan() !== 0 &&
|
|
||||||
(!this.gestute || this.gestute.capture())) {
|
|
||||||
this.onDragStart(ev);
|
|
||||||
this.captured = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Detection/capturing was not successful, aborting!
|
|
||||||
this.started = false;
|
|
||||||
this.captured = false;
|
|
||||||
this.pointerEvents.stop();
|
|
||||||
this.notCaptured(ev);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(this.detector, 'detector has to be valid');
|
||||||
|
const coord = pointerCoord(ev);
|
||||||
|
if (this.detector.detect(coord)) {
|
||||||
|
if (this.detector.pan() !== 0) {
|
||||||
|
if (!this.tryToCapture(ev)) {
|
||||||
|
this.abort(ev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pointerUp(ev: any) {
|
pointerUp(ev: any) {
|
||||||
@ -151,6 +156,26 @@ export class PanGesture {
|
|||||||
this.started = false;
|
this.started = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tryToCapture(ev: any): boolean {
|
||||||
|
assert(this.started === true, 'started has be true');
|
||||||
|
assert(this.captured === false, 'captured has be false');
|
||||||
|
|
||||||
|
if (this.gestute && !this.gestute.capture()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this.onDragStart(ev);
|
||||||
|
this.captured = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
abort(ev: any) {
|
||||||
|
this.started = false;
|
||||||
|
this.captured = false;
|
||||||
|
this.gestute.release();
|
||||||
|
this.pointerEvents.stop();
|
||||||
|
this.notCaptured(ev);
|
||||||
|
}
|
||||||
|
|
||||||
getNativeElement(): HTMLElement {
|
getNativeElement(): HTMLElement {
|
||||||
return this.element;
|
return this.element;
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,9 @@ export const GESTURE_ITEM_SWIPE = 'item-swipe';
|
|||||||
/** @private */
|
/** @private */
|
||||||
export const GESTURE_REFRESHER = 'refresher';
|
export const GESTURE_REFRESHER = 'refresher';
|
||||||
|
|
||||||
|
/** @private */
|
||||||
|
export const GESTURE_TOGGLE = 'toggle';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
@ -24,11 +27,13 @@ export const enum GesturePriority {
|
|||||||
Normal = 0,
|
Normal = 0,
|
||||||
High = 10,
|
High = 10,
|
||||||
VeryHigh = 20,
|
VeryHigh = 20,
|
||||||
|
VeryVeryHigh = 30,
|
||||||
|
|
||||||
SlidingItem = Low,
|
SlidingItem = Low,
|
||||||
MenuSwipe = High,
|
MenuSwipe = High,
|
||||||
GoBackSwipe = VeryHigh,
|
GoBackSwipe = VeryHigh,
|
||||||
Refresher = Normal,
|
Refresher = Normal,
|
||||||
|
Toggle = VeryVeryHigh
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2,15 +2,17 @@ import { PointerCoordinates } from '../util/dom';
|
|||||||
|
|
||||||
|
|
||||||
export class PanRecognizer {
|
export class PanRecognizer {
|
||||||
|
|
||||||
private startCoord: PointerCoordinates;
|
private startCoord: PointerCoordinates;
|
||||||
private dirty: boolean = false;
|
private dirty: boolean = false;
|
||||||
private threshold: number;
|
private threshold: number;
|
||||||
private maxCosine: number;
|
private maxCosine: number;
|
||||||
|
|
||||||
private _angle: any = 0;
|
private _angle: any = 0;
|
||||||
private _isPan: number = 0;
|
private _isPan: number = 0;
|
||||||
|
|
||||||
constructor(private direction: string, threshold: number, maxAngle: number) {
|
constructor(private direction: string, threshold: number, maxAngle: number) {
|
||||||
let radians = maxAngle * (Math.PI / 180);
|
const radians = maxAngle * (Math.PI / 180);
|
||||||
this.maxCosine = Math.cos(radians);
|
this.maxCosine = Math.cos(radians);
|
||||||
this.threshold = threshold * threshold;
|
this.threshold = threshold * threshold;
|
||||||
}
|
}
|
||||||
@ -26,12 +28,13 @@ export class PanRecognizer {
|
|||||||
if (!this.dirty) {
|
if (!this.dirty) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
let deltaX = (coord.x - this.startCoord.x);
|
const deltaX = (coord.x - this.startCoord.x);
|
||||||
let deltaY = (coord.y - this.startCoord.y);
|
const deltaY = (coord.y - this.startCoord.y);
|
||||||
let distance = deltaX * deltaX + deltaY * deltaY;
|
const distance = deltaX * deltaX + deltaY * deltaY;
|
||||||
|
|
||||||
if (distance >= this.threshold) {
|
if (distance >= this.threshold) {
|
||||||
let angle = Math.atan2(deltaY, deltaX);
|
var angle = Math.atan2(deltaY, deltaX);
|
||||||
let cosine = (this.direction === 'y')
|
var cosine = (this.direction === 'y')
|
||||||
? Math.sin(angle)
|
? Math.sin(angle)
|
||||||
: Math.cos(angle);
|
: Math.cos(angle);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user