From aa96e35a3b8b7ceff9913f50c8a3b0e820238a59 Mon Sep 17 00:00:00 2001 From: maliang Date: Fri, 9 Aug 2019 10:36:04 +0800 Subject: [PATCH] Create movable-view --- .../runtime/components/movable-view/index.js | 715 ++++++++++++++++++ .../components/movable-view/touchtrack.js | 83 ++ .../runtime/components/movable-view/utils.js | 297 ++++++++ 3 files changed, 1095 insertions(+) create mode 100755 src/platforms/app-plus-nvue/runtime/components/movable-view/index.js create mode 100755 src/platforms/app-plus-nvue/runtime/components/movable-view/touchtrack.js create mode 100755 src/platforms/app-plus-nvue/runtime/components/movable-view/utils.js diff --git a/src/platforms/app-plus-nvue/runtime/components/movable-view/index.js b/src/platforms/app-plus-nvue/runtime/components/movable-view/index.js new file mode 100755 index 000000000..120725c1b --- /dev/null +++ b/src/platforms/app-plus-nvue/runtime/components/movable-view/index.js @@ -0,0 +1,715 @@ +import touchtrack from './touchtrack' + +import { + Decline, + Friction, + STD +} from './utils' + +import { + emitter +} from '../../mixins' + +let requesting = false + +function requestAnimationFrame (callback) { + return setTimeout(callback, 16) +} + +function cancelAnimationFrame (id) { + clearTimeout(id) +} + +function _requestAnimationFrame (e) { + if (!requesting) { + requesting = true + requestAnimationFrame(function () { + e() + requesting = false + }) + } +} + +function v (a, b) { + return +((1000 * a - 1000 * b) / 1000).toFixed(1) +} + +function g (e, t, n) { + const i = function (e) { + if (e && e.id) { + cancelAnimationFrame(e.id) + } + if (e) { + e.cancelled = true + } + } + const r = { + id: 0, + cancelled: false + } + function fn (n, i, r, o) { + if (!n || !n.cancelled) { + r(i) + const a = e.done() + if (!a) { + if (!n.cancelled) { + (n.id = requestAnimationFrame(fn.bind(null, n, i, r, o))) + } + } + if (a && o) { + o(i) + } + } + } + fn(r, e, t, n) + return { + cancel: i.bind(null, r), + model: e + } +} + +function getMovableView (weex) { + const dom = weex.requireModule('dom') + const animation = weex.requireModule('animation') + + return { + name: 'MovableView', + mixins: [touchtrack, emitter], + props: { + direction: { + type: String, + default: 'none' + }, + inertia: { + type: [Boolean, String], + default: false + }, + outOfBounds: { + type: [Boolean, String], + default: false + }, + x: { + type: [Number, String], + default: 0 + }, + y: { + type: [Number, String], + default: 0 + }, + damping: { + type: [Number, String], + default: 20 + }, + friction: { + type: [Number, String], + default: 2 + }, + disabled: { + type: [Boolean, String], + default: false + }, + scale: { + type: [Boolean, String], + default: false + }, + scaleMin: { + type: [Number, String], + default: 0.5 + }, + scaleMax: { + type: [Number, String], + default: 10 + }, + scaleValue: { + type: [Number, String], + default: 1 + }, + animation: { + type: [Boolean, String], + default: true + } + }, + data () { + return { + xSync: this._getPx(this.x), + ySync: this._getPx(this.y), + scaleValueSync: this._getScaleNumber(this.scaleValue), + width: 0, + height: 0, + minX: 0, + minY: 0, + maxX: 0, + maxY: 0 + } + }, + computed: { + dampingNumber () { + const val = Number(this.damping) + return isNaN(val) ? 20 : val + }, + frictionNumber () { + const val = Number(this.friction) + return isNaN(val) || val <= 0 ? 2 : val + }, + scaleMinNumber () { + const val = Number(this.scaleMin) + return isNaN(val) ? 0.5 : val + }, + scaleMaxNumber () { + const val = Number(this.scaleMax) + return isNaN(val) ? 10 : val + }, + xMove () { + return this.direction === 'all' || this.direction === 'horizontal' + }, + yMove () { + return this.direction === 'all' || this.direction === 'vertical' + } + }, + watch: { + x (val) { + this.xSync = this._getPx(val) + }, + xSync (val) { + this._setX(val) + }, + y (val) { + this.ySync = this._getPx(val) + }, + ySync (val) { + this._setY(val) + }, + scaleValue (val) { + this._setScaleValue(this._getScaleNumber(val)) + }, + scaleValueSync (val) { + this._setScaleValue(val) + }, + scaleMinNumber () { + this._setScaleMinOrMax() + }, + scaleMaxNumber () { + this._setScaleMinOrMax() + } + }, + created: function () { + this._offset = { + x: 0, + y: 0 + } + this._scaleOffset = { + x: 0, + y: 0 + } + this._translateX = 0 + this._translateY = 0 + this._scale = 1 + this._oldScale = 1 + + this._STD = new STD(1, 9 * Math.pow(this.dampingNumber, 2) / 40, this.dampingNumber) + this._friction = new Friction(1, this.frictionNumber) + this._declineX = new Decline() + this._declineY = new Decline() + this.__touchInfo = { + historyX: [0, 0], + historyY: [0, 0], + historyT: [0, 0] + } + this._rect = { + top: 0, + left: 0, + width: 0, + height: 0 + } + }, + mounted: function () { + this.touchtrack('_onTrack') + setTimeout(() => { + this._updateRect().then(() => { + this.setParent() + }) + }, 100) + this._friction.reconfigure(1, this.frictionNumber) + this._STD.reconfigure(1, 9 * Math.pow(this.dampingNumber, 2) / 40, this.dampingNumber) + }, + methods: { + _getPx (val) { + // if (/\d+[ur]px$/i.test(val)) { + // return uni.upx2px(parseFloat(val)) + // } + return Number(val) || 0 + }, + _getScaleNumber (val) { + val = Number(val) + return isNaN(val) ? 1 : val + }, + _setX: function (val) { + if (this.xMove) { + if (val + this._scaleOffset.x === this._translateX) { + return this._translateX + } + else { + if (this._SFA) { + this._SFA.cancel() + } + this._animationTo(val + this._scaleOffset.x, this.ySync + this._scaleOffset.y, this._scale) + } + } + return val + }, + _setY: function (val) { + if (this.yMove) { + if (val + this._scaleOffset.y === this._translateY) { + return this._translateY + } + else { + if (this._SFA) { + this._SFA.cancel() + } + this._animationTo(this.xSync + this._scaleOffset.x, val + this._scaleOffset.y, this._scale) + } + } + return val + }, + _setScaleMinOrMax: function () { + if (!this.scale) { + return false + } + this._updateScale(this._scale, true) + this._updateOldScale(this._scale) + }, + _setScaleValue: function (scale) { + if (!this.scale) { + return false + } + scale = this._adjustScale(scale) + this._updateScale(scale, true) + this._updateOldScale(scale) + return scale + }, + __handleTouchStart: function () { + if (!this._isScaling) { + if (!this.disabled) { + if (this._FA) { + this._FA.cancel() + } + if (this._SFA) { + this._SFA.cancel() + } + this.__touchInfo.historyX = [0, 0] + this.__touchInfo.historyY = [0, 0] + this.__touchInfo.historyT = [0, 0] + if (this.xMove) { + this.__baseX = this._translateX + } + if (this.yMove) { + this.__baseY = this._translateY + } + this._checkCanMove = null + this._firstMoveDirection = null + this._isTouching = true + } + } + }, + __handleTouchMove: function (event) { + const self = this + if (!this._isScaling && !this.disabled && this._isTouching) { + let x = this._translateX + let y = this._translateY + if (this._firstMoveDirection === null) { + this._firstMoveDirection = Math.abs(event.detail.dx / event.detail.dy) > 1 ? 'htouchmove' : 'vtouchmove' + } + if (this.xMove) { + x = event.detail.dx + this.__baseX + this.__touchInfo.historyX.shift() + this.__touchInfo.historyX.push(x) + if (!this.yMove) { + if (!null !== this._checkCanMove) { + if (Math.abs(event.detail.dx / event.detail.dy) > 1) { + this._checkCanMove = false + } + else { + this._checkCanMove = true + } + } + } + } + if (this.yMove) { + y = event.detail.dy + this.__baseY + this.__touchInfo.historyY.shift() + this.__touchInfo.historyY.push(y) + if (!this.xMove) { + if (!null !== this._checkCanMove) { + if (Math.abs(event.detail.dy / event.detail.dx) > 1) { + this._checkCanMove = false + } + else { + this._checkCanMove = true + } + } + } + } + this.__touchInfo.historyT.shift() + this.__touchInfo.historyT.push(event.detail.timeStamp) + + if (!this._checkCanMove) { + // event.preventDefault() + let source = 'touch' + if (x < this.minX) { + if (this.outOfBounds) { + source = 'touch-out-of-bounds' + x = this.minX - this._declineX.x(this.minX - x) + } + else { + x = this.minX + } + } + else if (x > this.maxX) { + if (this.outOfBounds) { + source = 'touch-out-of-bounds' + x = this.maxX + this._declineX.x(x - this.maxX) + } + else { + x = this.maxX + } + } + if (y < this.minY) { + if (this.outOfBounds) { + source = 'touch-out-of-bounds' + y = this.minY - this._declineY.x(this.minY - y) + } + else { + y = this.minY + } + } + else { + if (y > this.maxY) { + if (this.outOfBounds) { + source = 'touch-out-of-bounds' + y = this.maxY + this._declineY.x(y - this.maxY) + } + else { + y = this.maxY + } + } + } + _requestAnimationFrame(function () { + self._setTransform(x, y, self._scale, source) + }) + } + } + }, + __handleTouchEnd: function () { + const self = this + if (!this._isScaling && !this.disabled && this._isTouching) { + this._isTouching = false + if (!this._checkCanMove && !this._revise('out-of-bounds') && this.inertia) { + const xv = 1000 * (this.__touchInfo.historyX[1] - this.__touchInfo.historyX[0]) / (this.__touchInfo.historyT[1] - this.__touchInfo.historyT[0]) + const yv = 1000 * (this.__touchInfo.historyY[1] - this.__touchInfo.historyY[0]) / (this.__touchInfo.historyT[1] - this.__touchInfo.historyT[0]) + this._friction.setV(xv, yv) + this._friction.setS(this._translateX, this._translateY) + const x0 = this._friction.delta().x + const y0 = this._friction.delta().y + let x = x0 + this._translateX + let y = y0 + this._translateY + if (x < this.minX) { + x = this.minX + y = this._translateY + (this.minX - this._translateX) * y0 / x0 + } + else { + if (x > this.maxX) { + x = this.maxX + y = this._translateY + (this.maxX - this._translateX) * y0 / x0 + } + } + if (y < this.minY) { + y = this.minY + x = this._translateX + (this.minY - this._translateY) * x0 / y0 + } + else { + if (y > this.maxY) { + y = this.maxY + x = this._translateX + (this.maxY - this._translateY) * x0 / y0 + } + } + this._friction.setEnd(x, y) + this._FA = g(this._friction, function () { + const t = self._friction.s() + const x = t.x + const y = t.y + self._setTransform(x, y, self._scale, 'friction') + }, function () { + self._FA.cancel() + }) + } + } + }, + _onTrack: function (event) { + switch (event.detail.state) { + case 'start': + this.__handleTouchStart() + break + case 'move': + this.__handleTouchMove(event) + break + case 'end': + this.__handleTouchEnd() + } + }, + _getLimitXY: function (x, y) { + let outOfBounds = false + if (x > this.maxX) { + x = this.maxX + outOfBounds = true + } + else { + if (x < this.minX) { + x = this.minX + outOfBounds = true + } + } + if (y > this.maxY) { + y = this.maxY + outOfBounds = true + } + else { + if (y < this.minY) { + y = this.minY + outOfBounds = true + } + } + return { + x, + y, + outOfBounds + } + }, + setParent: function () { + if (!this.$parent.__isMounted) { + return + } + if (this._FA) { + this._FA.cancel() + } + if (this._SFA) { + this._SFA.cancel() + } + const scale = this.scale ? this.scaleValueSync : 1 + this._updateOffset() + this._updateWH(scale) + this._updateBoundary() + this._translateX = this.xSync + this._scaleOffset.x + this._translateY = this.ySync + this._scaleOffset.y + const limitXY = this._getLimitXY(this._translateX, this._translateY) + const x = limitXY.x + const y = limitXY.y + this._setTransform(x, y, scale, '', true) + this._updateOldScale(scale) + }, + _updateOffset: function () { + this._offset.x = this._rect.left - this.$parent.left + this._offset.y = this._rect.top - this.$parent.top + }, + _updateWH: function (scale) { + scale = scale || this._scale + scale = this._adjustScale(scale) + const rect = this._rect + this.height = rect.height / this._scale + this.width = rect.width / this._scale + const height = this.height * scale + const width = this.width * scale + this._scaleOffset.x = (width - this.width) / 2 + this._scaleOffset.y = (height - this.height) / 2 + }, + _updateBoundary: function () { + const x = 0 - this._offset.x + this._scaleOffset.x + const width = this.$parent.width - this.width - this._offset.x - this._scaleOffset.x + this.minX = Math.min(x, width) + this.maxX = Math.max(x, width) + const y = 0 - this._offset.y + this._scaleOffset.y + const height = this.$parent.height - this.height - this._offset.y - this._scaleOffset.y + this.minY = Math.min(y, height) + this.maxY = Math.max(y, height) + }, + _beginScale: function () { + this._isScaling = true + }, + _endScale: function () { + this._isScaling = false + this._updateOldScale(this._scale) + }, + _setScale: function (scale) { + if (this.scale) { + scale = this._adjustScale(scale) + scale = this._oldScale * scale + this._beginScale() + this._updateScale(scale) + } + }, + _updateScale: function (scale, animat) { + const self = this + if (this.scale) { + scale = this._adjustScale(scale) + this._updateWH(scale) + this._updateBoundary() + const limitXY = this._getLimitXY(this._translateX, this._translateY) + const x = limitXY.x + const y = limitXY.y + if (animat) { + this._animationTo(x, y, scale, '', true, true) + } + else { + _requestAnimationFrame(function () { + self._setTransform(x, y, scale, '', true, true) + }) + } + } + }, + _updateOldScale: function (scale) { + this._oldScale = scale + }, + _adjustScale: function (scale) { + scale = Math.max(0.5, this.scaleMinNumber, scale) + scale = Math.min(10, this.scaleMaxNumber, scale) + return scale + }, + _animationTo: function (x, y, scale, source, r, o) { + const self = this + if (this._FA) { + this._FA.cancel() + } + if (this._SFA) { + this._SFA.cancel() + } + if (!this.xMove) { + x = this._translateX + } + if (!this.yMove) { + y = this._translateY + } + if (!this.scale) { + scale = this._scale + } + const limitXY = this._getLimitXY(x, y) + x = limitXY.x + y = limitXY.y + if (!this.animation) { + this._setTransform(x, y, scale, source, r, o) + return + } + this._STD._springX._solution = null + this._STD._springY._solution = null + this._STD._springScale._solution = null + this._STD._springX._endPosition = this._translateX + this._STD._springY._endPosition = this._translateY + this._STD._springScale._endPosition = this._scale + this._STD.setEnd(x, y, scale, 1) + this._SFA = g(this._STD, function () { + const data = self._STD.x() + const x = data.x + const y = data.y + const scale = data.scale + self._setTransform(x, y, scale, source, r, o) + }, function () { + self._SFA.cancel() + }) + }, + _revise: function (source) { + const limitXY = this._getLimitXY(this._translateX, this._translateY) + const x = limitXY.x + const y = limitXY.y + const outOfBounds = limitXY.outOfBounds + if (outOfBounds) { + this._animationTo(x, y, this._scale, source) + } + return outOfBounds + }, + _setTransform: function (x, y, scale, source = '', r, o) { + if (!(x !== null && x.toString() !== 'NaN' && typeof x === 'number')) { + x = this._translateX || 0 + } + if (!(y !== null && y.toString() !== 'NaN' && typeof y === 'number')) { + y = this._translateY || 0 + } + x = Number(x.toFixed(1)) + y = Number(y.toFixed(1)) + scale = Number(scale.toFixed(1)) + if (!(this._translateX === x && this._translateY === y)) { + if (!r) { + this.$trigger('change', { + x: v(x, this._scaleOffset.x), + y: v(y, this._scaleOffset.y), + source: source + }) + } + } + if (!this.scale) { + scale = this._scale + } + scale = this._adjustScale(scale) + scale = +scale.toFixed(3) + if (o && scale !== this._scale) { + this.$trigger('scale', { + x: x, + y: y, + scale: scale + }) + } + const transform = `translate(${x}px, ${y}px) scale(${scale})` + animation.transition(this.$refs.el, { + styles: { + transform + }, + duration: 0, + delay: 0 + }) + this._translateX = x + this._translateY = y + this._scale = scale + }, + _touchstart () { + this.$parent.touchItem = this + }, + _getComponentSize (el) { + return new Promise((resolve) => { + dom.getComponentRect(el, ({ size }) => { + resolve(size) + }) + }) + }, + _updateRect () { + return this._getComponentSize(this.$refs.el).then(rect => { + this._rect = rect + }) + } + }, + render (createElement) { + const events = { + touchstart: this._touchstart + // touchend: this.touchend, + // touchend: this.touchend + } + return createElement('div', this._g({ + ref: 'el', + on: events, + staticClass: ['uni-movable-view'], + staticStyle: { + transformOrigin: 'center' + } + }, this.$listeners), this.$slots.default, 2) + }, + style: { + 'uni-movable-view': { + position: 'absolute', + top: '0px', + left: '0px', + width: '10px', + height: '10px' + } + } + } +} + +export default function init (Vue, weex) { + Vue.component('movable-view', getMovableView(weex)) +} diff --git a/src/platforms/app-plus-nvue/runtime/components/movable-view/touchtrack.js b/src/platforms/app-plus-nvue/runtime/components/movable-view/touchtrack.js new file mode 100755 index 000000000..0964f4362 --- /dev/null +++ b/src/platforms/app-plus-nvue/runtime/components/movable-view/touchtrack.js @@ -0,0 +1,83 @@ +export default { + methods: { + touchtrack: function (method) { + const self = this + let x0 = 0 + let y0 = 0 + let x1 = 0 + let y1 = 0 + const fn = function ($event, state, x, y) { + if (self[method]({ + target: $event.target, + currentTarget: $event.currentTarget, + stopPropagation: $event.stopPropagation.bind($event), + touches: $event.touches, + changedTouches: $event.changedTouches, + detail: { + state, + x0: x, + y0: y, + dx: x - x0, + dy: y - y0, + ddx: x - x1, + ddy: y - y1, + timeStamp: $event.timeStamp + } + }) === false) { + return false + } + } + + let $eventOld = null + this.addListener('touchstart', function ($event) { + if (!$eventOld) { + $eventOld = $event + x0 = x1 = $event.touches[0].pageX + y0 = y1 = $event.touches[0].pageY + return fn($event, 'start', x0, y0) + } + }) + this.addListener('touchmove', function ($event) { + if ($eventOld) { + const res = fn($event, 'move', $event.touches[0].pageX, $event.touches[0].pageY) + x1 = $event.touches[0].pageX + y1 = $event.touches[0].pageY + return res + } + }) + this.addListener('touchend', function ($event) { + if ($eventOld) { + $eventOld = null + return fn($event, 'end', $event.changedTouches[0].pageX, $event.changedTouches[0].pageY) + } + }) + }, + touchstart ($event) { + this.callback('touchstart', $event) + }, + touchmove ($event) { + this.callback('touchmove', $event) + }, + touchend ($event) { + this.callback('touchend', $event) + }, + addListener (type, callback) { + this.__event[type] = function ($event) { + if (typeof callback === 'function') { + $event.touches = $event.changedTouches + if (callback($event) === false) { + $event.stopPropagation() + } + } + } + }, + callback (type, $event) { + if (this.__event[type]) { + this.__event[type]($event) + } + } + }, + created () { + this.__event = {} + } +} diff --git a/src/platforms/app-plus-nvue/runtime/components/movable-view/utils.js b/src/platforms/app-plus-nvue/runtime/components/movable-view/utils.js new file mode 100755 index 000000000..1dd8ca432 --- /dev/null +++ b/src/platforms/app-plus-nvue/runtime/components/movable-view/utils.js @@ -0,0 +1,297 @@ +function e (e, t, n) { + return e > t - n && e < t + n +} + +function t (t, n) { + return e(t, 0, n) +} + +export function Decline () { } +Decline.prototype.x = function (e) { + return Math.sqrt(e) +} + +export function Friction (e, t) { + this._m = e + this._f = 1e3 * t + this._startTime = 0 + this._v = 0 +} +Friction.prototype.setV = function (x, y) { + const n = Math.pow(Math.pow(x, 2) + Math.pow(y, 2), 0.5) + this._x_v = x + this._y_v = y + this._x_a = -this._f * this._x_v / n + this._y_a = -this._f * this._y_v / n + this._t = Math.abs(x / this._x_a) || Math.abs(y / this._y_a) + this._lastDt = null + this._startTime = (new Date()).getTime() +} +Friction.prototype.setS = function (x, y) { + this._x_s = x + this._y_s = y +} +Friction.prototype.s = function (t) { + if (undefined === t) { + t = ((new Date()).getTime() - this._startTime) / 1e3 + } + if (t > this._t) { + t = this._t + this._lastDt = t + } + let x = this._x_v * t + 0.5 * this._x_a * Math.pow(t, 2) + this._x_s + let y = this._y_v * t + 0.5 * this._y_a * Math.pow(t, 2) + this._y_s + if ((this._x_a > 0 && x < this._endPositionX) || (this._x_a < 0 && x > this._endPositionX)) { + x = this._endPositionX + } + if ((this._y_a > 0 && y < this._endPositionY) || (this._y_a < 0 && y > this._endPositionY)) { + y = this._endPositionY + } + return { + x, + y + } +} +Friction.prototype.ds = function (t) { + if (undefined === t) { + t = ((new Date()).getTime() - this._startTime) / 1e3 + } + if (t > this._t) { + t = this._t + } + return { + dx: this._x_v + this._x_a * t, + dy: this._y_v + this._y_a * t + } +} +Friction.prototype.delta = function () { + return { + x: -1.5 * Math.pow(this._x_v, 2) / this._x_a || 0, + y: -1.5 * Math.pow(this._y_v, 2) / this._y_a || 0 + } +} +Friction.prototype.dt = function () { + return -this._x_v / this._x_a +} +Friction.prototype.done = function () { + const t = e(this.s().x, this._endPositionX) || e(this.s().y, this._endPositionY) || this._lastDt === this._t + this._lastDt = null + return t +} +Friction.prototype.setEnd = function (x, y) { + this._endPositionX = x + this._endPositionY = y +} +Friction.prototype.reconfigure = function (m, f) { + this._m = m + this._f = 1e3 * f +} + +export function Spring (m, k, c) { + this._m = m + this._k = k + this._c = c + this._solution = null + this._endPosition = 0 + this._startTime = 0 +} +Spring.prototype._solve = function (e, t) { + const n = this._c + const i = this._m + const r = this._k + const o = n * n - 4 * i * r + if (o === 0) { + const a = -n / (2 * i) + const s = e + const l = t / (a * e) + return { + x: function (e) { + return (s + l * e) * Math.pow(Math.E, a * e) + }, + dx: function (e) { + const t = Math.pow(Math.E, a * e) + return a * (s + l * e) * t + l * t + } + } + } + if (o > 0) { + const c = (-n - Math.sqrt(o)) / (2 * i) + const u = (-n + Math.sqrt(o)) / (2 * i) + const d = (t - c * e) / (u - c) + const h = e - d + return { + x: function (e) { + let t + let n + if (e === this._t) { + t = this._powER1T + n = this._powER2T + } + this._t = e + if (!t) { + t = this._powER1T = Math.pow(Math.E, c * e) + } + if (!n) { + n = this._powER2T = Math.pow(Math.E, u * e) + } + return h * t + d * n + }, + dx: function (e) { + let t + let n + if (e === this._t) { + t = this._powER1T + n = this._powER2T + } + this._t = e + if (!t) { + t = this._powER1T = Math.pow(Math.E, c * e) + } + if (!n) { + n = this._powER2T = Math.pow(Math.E, u * e) + } + return h * c * t + d * u * n + } + } + } + const p = Math.sqrt(4 * i * r - n * n) / (2 * i) + const f = -n / 2 * i + const v = e + const g = (t - f * e) / p + return { + x: function (e) { + return Math.pow(Math.E, f * e) * (v * Math.cos(p * e) + g * Math.sin(p * e)) + }, + dx: function (e) { + const t = Math.pow(Math.E, f * e) + const n = Math.cos(p * e) + const i = Math.sin(p * e) + return t * (g * p * n - v * p * i) + f * t * (g * i + v * n) + } + } +} +Spring.prototype.x = function (e) { + if (undefined === e) { + e = ((new Date()).getTime() - this._startTime) / 1e3 + } + return this._solution ? this._endPosition + this._solution.x(e) : 0 +} +Spring.prototype.dx = function (e) { + if (undefined === e) { + e = ((new Date()).getTime() - this._startTime) / 1e3 + } + return this._solution ? this._solution.dx(e) : 0 +} +Spring.prototype.setEnd = function (e, n, i) { + if (!i) { + i = (new Date()).getTime() + } + if (e !== this._endPosition || !t(n, 0.1)) { + n = n || 0 + let r = this._endPosition + if (this._solution) { + if (t(n, 0.1)) { + n = this._solution.dx((i - this._startTime) / 1e3) + } + r = this._solution.x((i - this._startTime) / 1e3) + if (t(n, 0.1)) { + n = 0 + } + if (t(r, 0.1)) { + r = 0 + } + r += this._endPosition + } + if (!(this._solution && t(r - e, 0.1) && t(n, 0.1))) { + this._endPosition = e + this._solution = this._solve(r - this._endPosition, n) + this._startTime = i + } + } +} +Spring.prototype.snap = function (e) { + this._startTime = (new Date()).getTime() + this._endPosition = e + this._solution = { + x: function () { + return 0 + }, + dx: function () { + return 0 + } + } +} +Spring.prototype.done = function (n) { + if (!n) { + n = (new Date()).getTime() + } + return e(this.x(), this._endPosition, 0.1) && t(this.dx(), 0.1) +} +Spring.prototype.reconfigure = function (m, t, c) { + this._m = m + this._k = t + this._c = c + if (!this.done()) { + this._solution = this._solve(this.x() - this._endPosition, this.dx()) + this._startTime = (new Date()).getTime() + } +} +Spring.prototype.springConstant = function () { + return this._k +} +Spring.prototype.damping = function () { + return this._c +} +Spring.prototype.configuration = function () { + function e (e, t) { + e.reconfigure(1, t, e.damping()) + } + + function t (e, t) { + e.reconfigure(1, e.springConstant(), t) + } + return [{ + label: 'Spring Constant', + read: this.springConstant.bind(this), + write: e.bind(this, this), + min: 100, + max: 1e3 + }, { + label: 'Damping', + read: this.damping.bind(this), + write: t.bind(this, this), + min: 1, + max: 500 + }] +} + +export function STD (e, t, n) { + this._springX = new Spring(e, t, n) + this._springY = new Spring(e, t, n) + this._springScale = new Spring(e, t, n) + this._startTime = 0 +} +STD.prototype.setEnd = function (e, t, n, i) { + const r = (new Date()).getTime() + this._springX.setEnd(e, i, r) + this._springY.setEnd(t, i, r) + this._springScale.setEnd(n, i, r) + this._startTime = r +} +STD.prototype.x = function () { + const e = ((new Date()).getTime() - this._startTime) / 1e3 + return { + x: this._springX.x(e), + y: this._springY.x(e), + scale: this._springScale.x(e) + } +} +STD.prototype.done = function () { + const e = (new Date()).getTime() + return this._springX.done(e) && this._springY.done(e) && this._springScale.done(e) +} +STD.prototype.reconfigure = function (e, t, n) { + this._springX.reconfigure(e, t, n) + this._springY.reconfigure(e, t, n) + this._springScale.reconfigure(e, t, n) +} -- GitLab