@rafaelaugusto/jolt 0.1.0-beta.6 → 0.1.0-beta.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -4
- package/dist/animate.d.ts +2 -0
- package/dist/animate.d.ts.map +1 -1
- package/dist/animate.js +7 -2
- package/dist/animate.js.map +1 -1
- package/dist/drag-input.d.ts +15 -0
- package/dist/drag-input.d.ts.map +1 -0
- package/dist/drag-input.js +66 -0
- package/dist/drag-input.js.map +1 -0
- package/dist/drag-physics.d.ts +20 -0
- package/dist/drag-physics.d.ts.map +1 -0
- package/dist/drag-physics.js +75 -0
- package/dist/drag-physics.js.map +1 -0
- package/dist/drag.d.ts +58 -0
- package/dist/drag.d.ts.map +1 -0
- package/dist/drag.js +324 -0
- package/dist/drag.js.map +1 -0
- package/dist/driver/ios.d.ts.map +1 -1
- package/dist/driver/ios.js +38 -6
- package/dist/driver/ios.js.map +1 -1
- package/dist/engine.d.ts +2 -0
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +9 -0
- package/dist/engine.js.map +1 -1
- package/dist/image-effect-native.d.ts +26 -0
- package/dist/image-effect-native.d.ts.map +1 -0
- package/dist/image-effect-native.js +73 -0
- package/dist/image-effect-native.js.map +1 -0
- package/dist/image-effect.d.ts +50 -0
- package/dist/image-effect.d.ts.map +1 -0
- package/dist/image-effect.js +290 -0
- package/dist/image-effect.js.map +1 -0
- package/dist/image-owner.d.ts +3 -0
- package/dist/image-owner.d.ts.map +1 -0
- package/dist/image-owner.js +11 -0
- package/dist/image-owner.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/interaction-surface.d.ts +12 -0
- package/dist/interaction-surface.d.ts.map +1 -0
- package/dist/interaction-surface.js +36 -0
- package/dist/interaction-surface.js.map +1 -0
- package/dist/liquid.d.ts.map +1 -1
- package/dist/liquid.js +4 -0
- package/dist/liquid.js.map +1 -1
- package/dist/motion-scope.d.ts +37 -0
- package/dist/motion-scope.d.ts.map +1 -0
- package/dist/motion-scope.js +232 -0
- package/dist/motion-scope.js.map +1 -0
- package/dist/motion-states.d.ts +36 -0
- package/dist/motion-states.d.ts.map +1 -0
- package/dist/motion-states.js +165 -0
- package/dist/motion-states.js.map +1 -0
- package/dist/press.d.ts.map +1 -1
- package/dist/press.js +14 -3
- package/dist/press.js.map +1 -1
- package/dist/track.d.ts.map +1 -1
- package/dist/track.js +21 -4
- package/dist/track.js.map +1 -1
- package/dist/transition.d.ts +2 -1
- package/dist/transition.d.ts.map +1 -1
- package/dist/transition.js.map +1 -1
- package/dist/vue/index.d.ts +3 -1
- package/dist/vue/index.d.ts.map +1 -1
- package/dist/vue/index.js +17 -4
- package/dist/vue/index.js.map +1 -1
- package/dist/zoom-native.d.ts +30 -0
- package/dist/zoom-native.d.ts.map +1 -0
- package/dist/zoom-native.js +56 -0
- package/dist/zoom-native.js.map +1 -0
- package/dist/zoomable.d.ts +48 -0
- package/dist/zoomable.d.ts.map +1 -0
- package/dist/zoomable.js +249 -0
- package/dist/zoomable.js.map +1 -0
- package/package.json +2 -1
- package/platforms/android/java/org/rafaelaugusto/jolt/ImageEffect.java +288 -0
- package/platforms/android/java/org/rafaelaugusto/jolt/PhotoZoom.java +250 -0
- package/platforms/android/proguard-rules.pro +4 -0
- package/platforms/ios/build.xcconfig +1 -0
- package/platforms/ios/src/JoltImageEffect.h +25 -0
- package/platforms/ios/src/JoltImageEffect.mm +242 -0
- package/platforms/ios/src/JoltInteractiveAnimation.h +18 -0
- package/platforms/ios/src/JoltInteractiveAnimation.m +123 -0
- package/platforms/ios/src/JoltPhotoZoom.h +23 -0
- package/platforms/ios/src/JoltPhotoZoom.m +229 -0
- package/platforms/ios/src/module.modulemap +6 -0
- package/src/android-plugin.d.ts +78 -0
- package/src/animate.ts +15 -8
- package/src/drag-input.ts +74 -0
- package/src/drag-physics.ts +110 -0
- package/src/drag.ts +395 -0
- package/src/driver/ios.ts +57 -8
- package/src/engine.ts +11 -0
- package/src/image-effect-native.ts +129 -0
- package/src/image-effect.ts +336 -0
- package/src/image-owner.ts +12 -0
- package/src/index.ts +23 -0
- package/src/interaction-surface.ts +41 -0
- package/src/ios-plugin.d.ts +81 -0
- package/src/liquid.ts +4 -0
- package/src/motion-scope.ts +265 -0
- package/src/motion-states.ts +215 -0
- package/src/press.ts +15 -3
- package/src/track.ts +25 -4
- package/src/transition.ts +2 -0
- package/src/vue/index.ts +20 -3
- package/src/zoom-native.ts +124 -0
- package/src/zoomable.ts +330 -0
package/dist/zoomable.js
ADDED
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import { Application, Image, isIOS } from '@nativescript/core';
|
|
2
|
+
import { stop, toView } from './animate.js';
|
|
3
|
+
import { reducedMotion } from './config.js';
|
|
4
|
+
import { ownImage } from './image-owner.js';
|
|
5
|
+
import { createZoomDriver } from './zoom-native.js';
|
|
6
|
+
/** Makes an Image interactive without per-frame JS. Owns its transforms until disposed. */
|
|
7
|
+
export function zoomable(target, options = {}) {
|
|
8
|
+
const image = toView(target);
|
|
9
|
+
if (!(image instanceof Image))
|
|
10
|
+
throw new TypeError('zoomable() expects a NativeScript Image.');
|
|
11
|
+
if (!options || typeof options !== 'object')
|
|
12
|
+
throw new TypeError('Zoom options must be an object.');
|
|
13
|
+
const viewport = options.viewport
|
|
14
|
+
? toView(options.viewport)
|
|
15
|
+
: image.parent;
|
|
16
|
+
if (!viewport)
|
|
17
|
+
throw new TypeError('A zoom viewport is required before the image has a parent.');
|
|
18
|
+
const scale = options.scale ?? [1, 5];
|
|
19
|
+
if (!Array.isArray(scale) ||
|
|
20
|
+
scale.length !== 2 ||
|
|
21
|
+
!scale.every(Number.isFinite) ||
|
|
22
|
+
scale[0] < 1 ||
|
|
23
|
+
scale[1] < scale[0])
|
|
24
|
+
throw new RangeError('Zoom scale must be [minimum, maximum], with 1 <= minimum <= maximum.');
|
|
25
|
+
const doubleTap = options.doubleTap ?? 2.5;
|
|
26
|
+
if (!Number.isFinite(doubleTap) || doubleTap < 1)
|
|
27
|
+
throw new RangeError('Double-tap zoom must be a finite number >= 1.');
|
|
28
|
+
if (options.dismiss !== undefined &&
|
|
29
|
+
options.dismiss !== false &&
|
|
30
|
+
options.dismiss !== 'vertical')
|
|
31
|
+
throw new TypeError("Dismiss must be 'vertical' or false.");
|
|
32
|
+
for (const callback of [options.onDismiss, options.onSettled, options.onError])
|
|
33
|
+
if (callback !== undefined && typeof callback !== 'function')
|
|
34
|
+
throw new TypeError('Zoom callbacks must be functions.');
|
|
35
|
+
const backdrop = options.backdrop ? toView(options.backdrop) : undefined;
|
|
36
|
+
const chrome = options.chrome ? toView(options.chrome) : undefined;
|
|
37
|
+
if (backdrop === image ||
|
|
38
|
+
chrome === image ||
|
|
39
|
+
backdrop === viewport ||
|
|
40
|
+
chrome === viewport)
|
|
41
|
+
throw new TypeError('Dismissal backdrop/chrome must be separate from the image and viewport.');
|
|
42
|
+
if (image.parent && image.parent !== viewport)
|
|
43
|
+
throw new TypeError('The zoom viewport must be the image’s direct parent.');
|
|
44
|
+
if (image.rotate ||
|
|
45
|
+
image.rotateX ||
|
|
46
|
+
image.rotateY ||
|
|
47
|
+
image.scaleX <= 0 ||
|
|
48
|
+
image.scaleY <= 0)
|
|
49
|
+
throw new TypeError('Zoom supports positive axis-aligned image transforms; rotate an ancestor instead.');
|
|
50
|
+
return new Photo(image, viewport, { ...options, doubleTap, scale, backdrop, chrome });
|
|
51
|
+
}
|
|
52
|
+
class Photo {
|
|
53
|
+
image;
|
|
54
|
+
viewport;
|
|
55
|
+
options;
|
|
56
|
+
failure;
|
|
57
|
+
suspended = false;
|
|
58
|
+
driver;
|
|
59
|
+
stopped = false;
|
|
60
|
+
queued = false;
|
|
61
|
+
releaseOwnership = () => { };
|
|
62
|
+
generation = 0;
|
|
63
|
+
reduceObserver;
|
|
64
|
+
latest;
|
|
65
|
+
constructor(image, viewport, options) {
|
|
66
|
+
this.image = image;
|
|
67
|
+
this.viewport = viewport;
|
|
68
|
+
this.options = options;
|
|
69
|
+
this.latest = { x: 0, y: 0, scale: options.scale[0], progress: 0, pending: false };
|
|
70
|
+
this.releaseOwnership = ownImage(image, this.dispose);
|
|
71
|
+
stop(image, ['x', 'y', 'scaleX', 'scaleY']);
|
|
72
|
+
for (const view of [image, viewport]) {
|
|
73
|
+
view.on('loaded', this.schedule);
|
|
74
|
+
view.on('layoutChanged', this.schedule);
|
|
75
|
+
view.on('unloaded', this.dispose);
|
|
76
|
+
}
|
|
77
|
+
image.on('imageSourceChange', this.sourceChanged);
|
|
78
|
+
image.on('stretchChange', this.sourceChanged);
|
|
79
|
+
Application.on(Application.suspendEvent, this.suspend);
|
|
80
|
+
Application.on(Application.resumeEvent, this.resume);
|
|
81
|
+
if (isIOS)
|
|
82
|
+
this.reduceObserver = Application.ios.addNotificationObserver(UIAccessibilityReduceMotionStatusDidChangeNotification, this.preferences);
|
|
83
|
+
this.schedule();
|
|
84
|
+
}
|
|
85
|
+
get ready() {
|
|
86
|
+
return !!this.driver;
|
|
87
|
+
}
|
|
88
|
+
get error() {
|
|
89
|
+
return this.failure;
|
|
90
|
+
}
|
|
91
|
+
get state() {
|
|
92
|
+
return { ...(this.driver?.state ?? this.latest) };
|
|
93
|
+
}
|
|
94
|
+
get nativeController() {
|
|
95
|
+
return this.driver?.native;
|
|
96
|
+
}
|
|
97
|
+
zoomTo(value, focus, animated = true) {
|
|
98
|
+
if (!Number.isFinite(value) ||
|
|
99
|
+
value < this.options.scale[0] ||
|
|
100
|
+
value > this.options.scale[1])
|
|
101
|
+
throw new RangeError('Zoom must be within the configured scale range.');
|
|
102
|
+
if (focus && (!Number.isFinite(focus.x) || !Number.isFinite(focus.y)))
|
|
103
|
+
throw new RangeError('Zoom focus must be finite.');
|
|
104
|
+
if (this.stopped)
|
|
105
|
+
return;
|
|
106
|
+
const size = this.viewport.getActualSize();
|
|
107
|
+
this.driver?.zoom(value, focus?.x ?? size.width / 2, focus?.y ?? size.height / 2, animated);
|
|
108
|
+
}
|
|
109
|
+
reset(animated = true) {
|
|
110
|
+
this.generation++;
|
|
111
|
+
this.driver?.reset(animated);
|
|
112
|
+
}
|
|
113
|
+
requestDismiss() {
|
|
114
|
+
this.driver?.requestDismiss();
|
|
115
|
+
}
|
|
116
|
+
cancelDismiss() {
|
|
117
|
+
this.reset(true);
|
|
118
|
+
}
|
|
119
|
+
dispose = () => {
|
|
120
|
+
if (this.stopped)
|
|
121
|
+
return;
|
|
122
|
+
this.stopped = true;
|
|
123
|
+
this.generation++;
|
|
124
|
+
this.latest = {
|
|
125
|
+
x: 0,
|
|
126
|
+
y: 0,
|
|
127
|
+
scale: this.options.scale[0],
|
|
128
|
+
progress: 0,
|
|
129
|
+
pending: false,
|
|
130
|
+
};
|
|
131
|
+
this.driver?.stop();
|
|
132
|
+
this.driver = undefined;
|
|
133
|
+
this.releaseOwnership();
|
|
134
|
+
for (const view of [this.image, this.viewport]) {
|
|
135
|
+
view.off('loaded', this.schedule);
|
|
136
|
+
view.off('layoutChanged', this.schedule);
|
|
137
|
+
view.off('unloaded', this.dispose);
|
|
138
|
+
}
|
|
139
|
+
this.image.off('imageSourceChange', this.sourceChanged);
|
|
140
|
+
this.image.off('stretchChange', this.sourceChanged);
|
|
141
|
+
Application.off(Application.suspendEvent, this.suspend);
|
|
142
|
+
Application.off(Application.resumeEvent, this.resume);
|
|
143
|
+
if (this.reduceObserver)
|
|
144
|
+
Application.ios.removeNotificationObserver(this.reduceObserver, UIAccessibilityReduceMotionStatusDidChangeNotification);
|
|
145
|
+
this.reduceObserver = undefined;
|
|
146
|
+
};
|
|
147
|
+
dismissed = (x, y, scale, velocity, progress) => {
|
|
148
|
+
if (this.stopped)
|
|
149
|
+
return;
|
|
150
|
+
const generation = ++this.generation;
|
|
151
|
+
const valid = () => !this.stopped && generation === this.generation;
|
|
152
|
+
const dismissal = {
|
|
153
|
+
state: { x, y, scale, progress, pending: true },
|
|
154
|
+
velocity,
|
|
155
|
+
cancel: () => {
|
|
156
|
+
if (!valid())
|
|
157
|
+
return false;
|
|
158
|
+
this.cancelDismiss();
|
|
159
|
+
return true;
|
|
160
|
+
},
|
|
161
|
+
complete: () => {
|
|
162
|
+
if (!valid())
|
|
163
|
+
return false;
|
|
164
|
+
this.dispose();
|
|
165
|
+
return true;
|
|
166
|
+
},
|
|
167
|
+
};
|
|
168
|
+
if (this.options.onDismiss)
|
|
169
|
+
this.options.onDismiss(dismissal);
|
|
170
|
+
else
|
|
171
|
+
dismissal.cancel();
|
|
172
|
+
};
|
|
173
|
+
settled = (x, y, scale) => {
|
|
174
|
+
if (this.stopped)
|
|
175
|
+
return;
|
|
176
|
+
this.generation++;
|
|
177
|
+
this.latest = { x, y, scale, progress: 0, pending: false };
|
|
178
|
+
this.options.onSettled?.({ ...this.latest });
|
|
179
|
+
};
|
|
180
|
+
sourceChanged = () => {
|
|
181
|
+
this.generation++;
|
|
182
|
+
this.driver?.stop();
|
|
183
|
+
this.driver = undefined;
|
|
184
|
+
this.schedule();
|
|
185
|
+
};
|
|
186
|
+
suspend = () => {
|
|
187
|
+
this.suspended = true;
|
|
188
|
+
this.generation++;
|
|
189
|
+
this.driver?.setActive(false);
|
|
190
|
+
};
|
|
191
|
+
resume = () => {
|
|
192
|
+
this.suspended = false;
|
|
193
|
+
this.preferences();
|
|
194
|
+
this.driver?.setActive(true);
|
|
195
|
+
this.schedule();
|
|
196
|
+
};
|
|
197
|
+
preferences = () => this.driver?.setReduced(reducedMotion());
|
|
198
|
+
schedule = () => {
|
|
199
|
+
if (this.stopped || this.queued)
|
|
200
|
+
return;
|
|
201
|
+
this.queued = true;
|
|
202
|
+
void Promise.resolve()
|
|
203
|
+
.then(() => {
|
|
204
|
+
this.queued = false;
|
|
205
|
+
if (this.stopped ||
|
|
206
|
+
this.suspended ||
|
|
207
|
+
!this.image.isLoaded ||
|
|
208
|
+
!this.viewport.isLoaded)
|
|
209
|
+
return;
|
|
210
|
+
if (this.driver) {
|
|
211
|
+
this.driver.layout();
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
if (this.image.parent !== this.viewport)
|
|
215
|
+
throw new TypeError('Zoom viewport must remain the direct image parent.');
|
|
216
|
+
const size = this.viewport.getActualSize();
|
|
217
|
+
const imageSize = this.image.getActualSize(), source = this.image.imageSource;
|
|
218
|
+
if (size.width <= 0 ||
|
|
219
|
+
size.height <= 0 ||
|
|
220
|
+
imageSize.width <= 0 ||
|
|
221
|
+
imageSize.height <= 0 ||
|
|
222
|
+
!source ||
|
|
223
|
+
!Number.isFinite(source.width) ||
|
|
224
|
+
!Number.isFinite(source.height) ||
|
|
225
|
+
source.width <= 0 ||
|
|
226
|
+
source.height <= 0)
|
|
227
|
+
return;
|
|
228
|
+
if (isIOS && typeof JoltPhotoZoom === 'undefined')
|
|
229
|
+
throw new Error('Rebuild the native app to install Jolt photo gestures.');
|
|
230
|
+
this.driver = createZoomDriver(this.image, this.viewport, {
|
|
231
|
+
minimum: this.options.scale[0],
|
|
232
|
+
maximum: this.options.scale[1],
|
|
233
|
+
doubleTap: this.options.doubleTap,
|
|
234
|
+
dismiss: this.options.dismiss === 'vertical',
|
|
235
|
+
reduced: reducedMotion(),
|
|
236
|
+
backdrop: this.options.backdrop,
|
|
237
|
+
chrome: this.options.chrome,
|
|
238
|
+
}, this.dismissed, this.settled);
|
|
239
|
+
})
|
|
240
|
+
.catch((error) => {
|
|
241
|
+
if (this.stopped)
|
|
242
|
+
return;
|
|
243
|
+
this.failure = error instanceof Error ? error : new Error(String(error));
|
|
244
|
+
this.dispose();
|
|
245
|
+
this.options.onError?.(this.failure);
|
|
246
|
+
});
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
//# sourceMappingURL=zoomable.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"zoomable.js","sourceRoot":"","sources":["../src/zoomable.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rafaelaugusto/jolt",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.8",
|
|
4
4
|
"description": "Native animations for NativeScript: presets, springs, keyframes and stagger in one call.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"nativescript",
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
"files": [
|
|
39
39
|
"dist",
|
|
40
40
|
"src",
|
|
41
|
+
"platforms/ios",
|
|
41
42
|
"platforms/android/java",
|
|
42
43
|
"platforms/android/include.gradle",
|
|
43
44
|
"platforms/android/proguard-rules.pro"
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
package org.rafaelaugusto.jolt;
|
|
2
|
+
|
|
3
|
+
import android.animation.Animator;
|
|
4
|
+
import android.animation.AnimatorListenerAdapter;
|
|
5
|
+
import android.animation.ValueAnimator;
|
|
6
|
+
import android.graphics.*;
|
|
7
|
+
import android.graphics.drawable.*;
|
|
8
|
+
import android.os.Build;
|
|
9
|
+
import android.view.MotionEvent;
|
|
10
|
+
import android.view.View;
|
|
11
|
+
import android.widget.ImageView;
|
|
12
|
+
|
|
13
|
+
/** Image-only GPU effects. All animation and pointer samples stay in Java. */
|
|
14
|
+
public final class ImageEffect {
|
|
15
|
+
public interface Listener { void onEnd(boolean completed, float progress); }
|
|
16
|
+
private static final String SHADER =
|
|
17
|
+
"uniform shader source; uniform shader destination; uniform float2 size;" +
|
|
18
|
+
"uniform float2 origin; uniform float2 displacement; uniform float progress;" +
|
|
19
|
+
"uniform float amplitude; uniform float radius; uniform float seed; uniform float mode;" +
|
|
20
|
+
"half4 main(float2 p) { float2 uv=p/size; float2 q=p;" +
|
|
21
|
+
"if(mode<1.5){ float2 v=p-origin; float d=length(v);" +
|
|
22
|
+
"float edge=clamp(min(min(p.x,size.x-p.x),min(p.y,size.y-p.y))/max(amplitude*2.,1.),0.,1.);" +
|
|
23
|
+
"if(mode<.5){float wave=d-progress*length(size);" +
|
|
24
|
+
"float a=sin(wave/max(radius*.12,1.))*exp(-pow(wave/max(radius,1.),2.))*amplitude*sin(progress*3.14159265);" +
|
|
25
|
+
"q-=v/max(d,1.)*a*edge;}else{q-=displacement*exp(-d*d/max(radius*radius,1.))*edge;}" +
|
|
26
|
+
"return source.eval(clamp(q,float2(.5),size-float2(.5)));}" +
|
|
27
|
+
"if(progress<=0.)return source.eval(p); if(progress>=1.)return destination.eval(p);" +
|
|
28
|
+
"float n=fract(sin(dot(floor(p/3.),float2(12.9898,78.233))+seed)*43758.5453);" +
|
|
29
|
+
"float a=smoothstep(n-.08,n+.08,progress*1.16-.08);" +
|
|
30
|
+
"return mix(source.eval(p),destination.eval(p),a);}";
|
|
31
|
+
|
|
32
|
+
private final ImageView view;
|
|
33
|
+
private final Listener listener;
|
|
34
|
+
private final int mode;
|
|
35
|
+
private final float amplitude, radius;
|
|
36
|
+
private final long duration;
|
|
37
|
+
private final Drawable original;
|
|
38
|
+
private final Bitmap originalBitmap;
|
|
39
|
+
private final ImageView.ScaleType originalScale;
|
|
40
|
+
private final View.OnTouchListener previousTouch;
|
|
41
|
+
private final boolean originalClickable;
|
|
42
|
+
private Bitmap source, destination, placeholderBitmap;
|
|
43
|
+
private RuntimeShader shader;
|
|
44
|
+
private EffectDrawable drawable;
|
|
45
|
+
private Drawable placeholder;
|
|
46
|
+
private ValueAnimator animator;
|
|
47
|
+
private boolean disposed, paused, suppressEnd, attached;
|
|
48
|
+
private float progress, goal = 1, x, y, dx, dy;
|
|
49
|
+
private int pointer = -1;
|
|
50
|
+
private float startX, startY, rawX, rawY;
|
|
51
|
+
private long frames, lastFrame, longestFrame;
|
|
52
|
+
private String failureReason="";
|
|
53
|
+
private final Path contentClip=new Path();
|
|
54
|
+
|
|
55
|
+
public ImageEffect(ImageView view, Bitmap from, Bitmap to, float[] geometry, int mode, float x, float y,
|
|
56
|
+
float amplitude, float radius, long duration, float seed,
|
|
57
|
+
View.OnTouchListener previousTouch, Listener listener) {
|
|
58
|
+
this.view=view; this.listener=listener; this.mode=mode;
|
|
59
|
+
this.amplitude=amplitude; this.radius=radius; this.duration=duration;
|
|
60
|
+
this.x=x; this.y=y; this.previousTouch=previousTouch;
|
|
61
|
+
original=view.getDrawable(); originalScale=view.getScaleType();
|
|
62
|
+
originalBitmap=from;
|
|
63
|
+
originalClickable=view.isClickable();
|
|
64
|
+
if(Build.VERSION.SDK_INT<33 || !view.isHardwareAccelerated() || from==null || original==null || original instanceof Animatable) {
|
|
65
|
+
failureReason="Requires API 33, hardware rendering and a static drawable ("+(original==null?"null":original.getClass().getName())+")";
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
int w=view.getWidth(), h=view.getHeight();
|
|
69
|
+
if(w<=0 || h<=0 || (long)w*h>4194304L) {failureReason="Image surface exceeds the pixel budget";return;}
|
|
70
|
+
try {
|
|
71
|
+
float left=view.getPaddingLeft(),top=view.getPaddingTop(),right=view.getPaddingRight(),bottom=view.getPaddingBottom();
|
|
72
|
+
float[] radii={
|
|
73
|
+
Math.max(0,geometry[2]-left),Math.max(0,geometry[2]-top),
|
|
74
|
+
Math.max(0,geometry[3]-right),Math.max(0,geometry[3]-top),
|
|
75
|
+
Math.max(0,geometry[4]-right),Math.max(0,geometry[4]-bottom),
|
|
76
|
+
Math.max(0,geometry[5]-left),Math.max(0,geometry[5]-bottom)
|
|
77
|
+
};
|
|
78
|
+
contentClip.addRoundRect(new RectF(left,top,w-right,h-bottom),radii,Path.Direction.CW);
|
|
79
|
+
source=raster(from,geometry[0],w,h);
|
|
80
|
+
if(mode==2) {
|
|
81
|
+
if(to==null) { releaseBitmaps(); return; }
|
|
82
|
+
destination=raster(to,geometry[1],w,h);
|
|
83
|
+
}
|
|
84
|
+
shader=new RuntimeShader(SHADER);
|
|
85
|
+
shader.setInputShader("source",new BitmapShader(source,Shader.TileMode.CLAMP,Shader.TileMode.CLAMP));
|
|
86
|
+
shader.setInputShader("destination",new BitmapShader(destination==null?source:destination,Shader.TileMode.CLAMP,Shader.TileMode.CLAMP));
|
|
87
|
+
shader.setFloatUniform("size",w,h); shader.setFloatUniform("mode",mode);
|
|
88
|
+
shader.setFloatUniform("amplitude",amplitude); shader.setFloatUniform("radius",radius);
|
|
89
|
+
shader.setFloatUniform("seed",seed);
|
|
90
|
+
drawable=new EffectDrawable(w,h);
|
|
91
|
+
drawable.setBounds(0,0,w,h);
|
|
92
|
+
update();
|
|
93
|
+
// NativeScript's ImageView rasterizes custom drawables on a software
|
|
94
|
+
// canvas. An overlay keeps RuntimeShader on the hardware canvas.
|
|
95
|
+
view.getOverlay().add(drawable);
|
|
96
|
+
attached=true;
|
|
97
|
+
// NativeScript's onDraw clips the canvas to its bitmap's mapped
|
|
98
|
+
// rectangle, including overlays. A full-content transparent alpha
|
|
99
|
+
// bitmap keeps that clip open in every stretch/rotation mode.
|
|
100
|
+
// Clearing the drawable leaves its cached bitmap shader invalid.
|
|
101
|
+
int pw=Math.max(1,w-view.getPaddingLeft()-view.getPaddingRight());
|
|
102
|
+
int ph=Math.max(1,h-view.getPaddingTop()-view.getPaddingBottom());
|
|
103
|
+
float angle=Math.abs(geometry[0]%180);
|
|
104
|
+
if(angle>45 && angle<135){int swap=pw;pw=ph;ph=swap;}
|
|
105
|
+
placeholderBitmap=Bitmap.createBitmap(pw,ph,Bitmap.Config.ALPHA_8);
|
|
106
|
+
view.setImageBitmap(placeholderBitmap);
|
|
107
|
+
placeholder=view.getDrawable();
|
|
108
|
+
if(mode==1) {
|
|
109
|
+
view.setClickable(true);
|
|
110
|
+
view.setOnTouchListener(this::touch);
|
|
111
|
+
}
|
|
112
|
+
} catch (RuntimeException | OutOfMemoryError error) {
|
|
113
|
+
failureReason=error.toString();
|
|
114
|
+
if(attached) restore(true);
|
|
115
|
+
releaseBitmaps(); shader=null; drawable=null;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** Match NativeScript's bitmap mapping without baking backgrounds/borders
|
|
120
|
+
* into the texture. The stationary inner corner mask is applied at draw time. */
|
|
121
|
+
private Bitmap raster(Bitmap bitmap,float rotation,int width,int height) {
|
|
122
|
+
Bitmap result=Bitmap.createBitmap(width,height,Bitmap.Config.ARGB_8888);
|
|
123
|
+
Canvas canvas=new Canvas(result);
|
|
124
|
+
float left=view.getPaddingLeft(),top=view.getPaddingTop();
|
|
125
|
+
float w=width-left-view.getPaddingRight(),h=height-top-view.getPaddingBottom();
|
|
126
|
+
float iw=bitmap.getWidth(),ih=bitmap.getHeight();
|
|
127
|
+
Matrix matrix=new Matrix();matrix.postRotate(rotation,iw/2,ih/2);
|
|
128
|
+
float angle=Math.abs(rotation%180);
|
|
129
|
+
if(angle>45 && angle<135){matrix.postTranslate((ih-iw)/2,(iw-ih)/2);float swap=iw;iw=ih;ih=swap;}
|
|
130
|
+
float sx=w/iw,sy=h/ih;
|
|
131
|
+
RectF visible=new RectF(left,top,left+w,top+h);
|
|
132
|
+
if(originalScale==ImageView.ScaleType.FIT_XY) matrix.postScale(sx,sy);
|
|
133
|
+
else if(originalScale==ImageView.ScaleType.MATRIX) visible.intersect(left,top,left+iw,top+ih);
|
|
134
|
+
else {
|
|
135
|
+
float scale=originalScale==ImageView.ScaleType.CENTER?1:originalScale==ImageView.ScaleType.CENTER_CROP?Math.max(sx,sy):Math.min(sx,sy);
|
|
136
|
+
matrix.postTranslate((w-iw)/2,(h-ih)/2);
|
|
137
|
+
matrix.postScale(scale,scale,w/2,h/2);
|
|
138
|
+
visible.intersect(left+(w-iw*scale)/2,top+(h-ih*scale)/2,left+(w+iw*scale)/2,top+(h+ih*scale)/2);
|
|
139
|
+
}
|
|
140
|
+
matrix.postTranslate(left,top);
|
|
141
|
+
BitmapShader pixels=new BitmapShader(bitmap,Shader.TileMode.CLAMP,Shader.TileMode.CLAMP);
|
|
142
|
+
pixels.setLocalMatrix(matrix);
|
|
143
|
+
Paint paint=new Paint(Paint.ANTI_ALIAS_FLAG|Paint.FILTER_BITMAP_FLAG);
|
|
144
|
+
paint.setShader(pixels);paint.setColorFilter(view.getColorFilter());
|
|
145
|
+
canvas.drawRect(visible,paint);
|
|
146
|
+
return result;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
public boolean isSupported() { return drawable!=null && !disposed; }
|
|
150
|
+
public String getFailureReason() { return failureReason; }
|
|
151
|
+
public float getProgress() { return progress; }
|
|
152
|
+
public long getFrames() { return frames; }
|
|
153
|
+
public long getLongestFrameNanos() { return longestFrame; }
|
|
154
|
+
public long getTextureBytes() { return source==null?0:(long)source.getAllocationByteCount()+(destination==null?0:destination.getAllocationByteCount())+(placeholderBitmap==null?0:placeholderBitmap.getAllocationByteCount()); }
|
|
155
|
+
public boolean isPaused() { return paused; }
|
|
156
|
+
|
|
157
|
+
public void play(float destinationProgress) {
|
|
158
|
+
if(disposed || drawable==null || mode==1) return;
|
|
159
|
+
goal=destinationProgress; paused=false;
|
|
160
|
+
animate(progress,goal,Math.max(1,(long)(duration*Math.abs(goal-progress))),false);
|
|
161
|
+
}
|
|
162
|
+
public void pause() {
|
|
163
|
+
paused=true;
|
|
164
|
+
if(animator!=null) animator.pause();
|
|
165
|
+
}
|
|
166
|
+
public void resume() {
|
|
167
|
+
if(disposed) return;
|
|
168
|
+
paused=false;
|
|
169
|
+
lastFrame=0;
|
|
170
|
+
if(animator!=null && animator.isPaused()) animator.resume();
|
|
171
|
+
}
|
|
172
|
+
public void seek(float value) {
|
|
173
|
+
if(disposed || mode==1) return;
|
|
174
|
+
boolean hold=paused;
|
|
175
|
+
cancelAnimator(); progress=value; update();
|
|
176
|
+
if(!hold) play(goal);
|
|
177
|
+
}
|
|
178
|
+
public void finish(float endpoint) {
|
|
179
|
+
if(disposed || mode==1) return;
|
|
180
|
+
cancelAnimator(); progress=endpoint; update();
|
|
181
|
+
listener.onEnd(true,progress);
|
|
182
|
+
}
|
|
183
|
+
public void stop() {
|
|
184
|
+
detach(true);
|
|
185
|
+
}
|
|
186
|
+
public void stopPreservingImage() {
|
|
187
|
+
detach(false);
|
|
188
|
+
}
|
|
189
|
+
private void detach(boolean restoreImage) {
|
|
190
|
+
if(disposed) return;
|
|
191
|
+
disposed=true; cancelAnimator();
|
|
192
|
+
restore(restoreImage); shader=null; drawable=null; releaseBitmaps();
|
|
193
|
+
}
|
|
194
|
+
private void restore(boolean restoreImage) {
|
|
195
|
+
if(attached && drawable!=null) view.getOverlay().remove(drawable);
|
|
196
|
+
if(restoreImage && attached && view.getDrawable()==placeholder) {
|
|
197
|
+
// NativeScript caches mBitmap separately from its drawable. Restoring
|
|
198
|
+
// only the drawable leaves the placeholder's geometry behind.
|
|
199
|
+
view.setImageBitmap(originalBitmap);
|
|
200
|
+
}
|
|
201
|
+
if(mode==1 && drawable!=null) {
|
|
202
|
+
view.setOnTouchListener(previousTouch); view.setClickable(originalClickable);
|
|
203
|
+
if(view.getParent()!=null) view.getParent().requestDisallowInterceptTouchEvent(false);
|
|
204
|
+
}
|
|
205
|
+
attached=false;
|
|
206
|
+
}
|
|
207
|
+
private void releaseBitmaps() {
|
|
208
|
+
// Draw commands may still reference them; let Android retire the backing storage safely.
|
|
209
|
+
source=null; destination=null; placeholderBitmap=null;
|
|
210
|
+
}
|
|
211
|
+
private void cancelAnimator() {
|
|
212
|
+
if(animator==null) return;
|
|
213
|
+
suppressEnd=true; animator.cancel(); animator=null; suppressEnd=false;
|
|
214
|
+
}
|
|
215
|
+
private void animate(float from,float to,long milliseconds,boolean settle) {
|
|
216
|
+
cancelAnimator();
|
|
217
|
+
lastFrame=0;
|
|
218
|
+
final float startDx=dx,startDy=dy;
|
|
219
|
+
animator=ValueAnimator.ofFloat(from,to);
|
|
220
|
+
animator.setDuration(milliseconds);
|
|
221
|
+
animator.setInterpolator(new android.view.animation.LinearInterpolator());
|
|
222
|
+
animator.addUpdateListener(a->{
|
|
223
|
+
float value=(float)a.getAnimatedValue();
|
|
224
|
+
if(settle) {float t=value; float decay=(float)(Math.exp(-8*t)*(1-t)); dx=startDx*decay; dy=startDy*decay;}
|
|
225
|
+
else progress=value;
|
|
226
|
+
update();
|
|
227
|
+
});
|
|
228
|
+
animator.addListener(new AnimatorListenerAdapter(){
|
|
229
|
+
@Override public void onAnimationEnd(Animator a){
|
|
230
|
+
if(suppressEnd || disposed) return;
|
|
231
|
+
animator=null;
|
|
232
|
+
if(settle) {dx=dy=0;update();}
|
|
233
|
+
else listener.onEnd(true,progress);
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
animator.start();
|
|
237
|
+
}
|
|
238
|
+
private boolean touch(View target,MotionEvent event) {
|
|
239
|
+
if(disposed || paused) return false;
|
|
240
|
+
int action=event.getActionMasked();
|
|
241
|
+
if(action==MotionEvent.ACTION_DOWN) {
|
|
242
|
+
cancelAnimator(); pointer=event.getPointerId(0);
|
|
243
|
+
x=startX=event.getX(); y=startY=event.getY(); dx=dy=rawX=rawY=0;
|
|
244
|
+
update(); return true;
|
|
245
|
+
}
|
|
246
|
+
int index=event.findPointerIndex(pointer);
|
|
247
|
+
if(action==MotionEvent.ACTION_MOVE && index>=0) {
|
|
248
|
+
float vx=event.getX(index)-startX,vy=event.getY(index)-startY;
|
|
249
|
+
rawX=vx;rawY=vy;
|
|
250
|
+
float length=(float)Math.hypot(vx,vy);
|
|
251
|
+
float factor=length==0?0:(float)(amplitude*Math.tanh(length/Math.max(amplitude,1))/length);
|
|
252
|
+
dx=vx*factor; dy=vy*factor; update(); return true;
|
|
253
|
+
}
|
|
254
|
+
if(action==MotionEvent.ACTION_POINTER_UP && event.getPointerId(event.getActionIndex())==pointer) {
|
|
255
|
+
int next=event.getActionIndex()==0?1:0;
|
|
256
|
+
if(next<event.getPointerCount()) {
|
|
257
|
+
pointer=event.getPointerId(next); startX=event.getX(next)-rawX;startY=event.getY(next)-rawY;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
if(action==MotionEvent.ACTION_UP || action==MotionEvent.ACTION_CANCEL) {
|
|
261
|
+
pointer=-1; animate(0,1,Math.min(600,duration),true);
|
|
262
|
+
if(action==MotionEvent.ACTION_UP) view.performClick();
|
|
263
|
+
}
|
|
264
|
+
return true;
|
|
265
|
+
}
|
|
266
|
+
private void update() {
|
|
267
|
+
if(shader==null || disposed) return;
|
|
268
|
+
shader.setFloatUniform("origin",x,y);
|
|
269
|
+
shader.setFloatUniform("displacement",dx,dy);
|
|
270
|
+
shader.setFloatUniform("progress",progress);
|
|
271
|
+
if(drawable!=null) drawable.invalidateSelf();
|
|
272
|
+
}
|
|
273
|
+
private final class EffectDrawable extends Drawable {
|
|
274
|
+
private final Paint paint=new Paint(Paint.ANTI_ALIAS_FLAG);
|
|
275
|
+
private final int width,height;
|
|
276
|
+
EffectDrawable(int width,int height) {this.width=width;this.height=height;paint.setShader(shader);}
|
|
277
|
+
@Override public int getIntrinsicWidth(){return width;}
|
|
278
|
+
@Override public int getIntrinsicHeight(){return height;}
|
|
279
|
+
@Override public void draw(Canvas canvas){
|
|
280
|
+
long now=System.nanoTime(); if(lastFrame!=0 && animator!=null && !paused) longestFrame=Math.max(longestFrame,now-lastFrame);
|
|
281
|
+
lastFrame=now; frames++;
|
|
282
|
+
canvas.drawPath(contentClip,paint);
|
|
283
|
+
}
|
|
284
|
+
@Override public void setAlpha(int alpha){paint.setAlpha(alpha);invalidateSelf();}
|
|
285
|
+
@Override public void setColorFilter(ColorFilter filter){paint.setColorFilter(filter);invalidateSelf();}
|
|
286
|
+
@Override public int getOpacity(){return PixelFormat.TRANSLUCENT;}
|
|
287
|
+
}
|
|
288
|
+
}
|