初始化

This commit is contained in:
SmallMain
2022-06-25 00:23:03 +08:00
commit ef0589e8e5
2264 changed files with 617829 additions and 0 deletions

View File

@@ -0,0 +1,267 @@
/****************************************************************************
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
const js = cc.js;
const Playable = require('./playable');
const { EventAnimCurve, EventInfo } = require('./animation-curves');
const WrapModeMask = require('./types').WrapModeMask;
const binarySearch = require('../core/utils/binary-search').binarySearchEpsilon;
// The actual animator for Animation Component
function AnimationAnimator (target, animation) {
Playable.call(this);
this.target = target;
this.animation = animation;
this._anims = new js.array.MutableForwardIterator([]);
}
js.extend(AnimationAnimator, Playable);
let p = AnimationAnimator.prototype;
p.playState = function (state, startTime) {
if (!state.clip) {
return;
}
if (!state.curveLoaded) {
initClipData(this.target, state);
}
state.animator = this;
state.play();
if (typeof startTime === 'number') {
state.setTime(startTime);
}
this.play();
};
p.stopStatesExcept = function (state) {
let iterator = this._anims;
let array = iterator.array;
for (iterator.i = 0; iterator.i < array.length; ++iterator.i) {
let anim = array[iterator.i];
if (anim === state) {
continue;
}
this.stopState(anim);
}
};
p.addAnimation = function (anim) {
let index = this._anims.array.indexOf(anim);
if (index === -1) {
this._anims.push(anim);
}
anim._setEventTarget(this.animation);
};
p.removeAnimation = function (anim) {
let index = this._anims.array.indexOf(anim);
if (index >= 0) {
this._anims.fastRemoveAt(index);
if (this._anims.array.length === 0) {
this.stop();
}
}
else {
cc.errorID(3907);
}
anim.animator = null;
};
p.sample = function () {
let iterator = this._anims;
let array = iterator.array;
for (iterator.i = 0; iterator.i < array.length; ++iterator.i) {
let anim = array[iterator.i];
anim.sample();
}
};
p.stopState = function (state) {
if (state) {
state.stop();
}
};
p.pauseState = function (state) {
if (state) {
state.pause();
}
};
p.resumeState = function (state) {
if (state) {
state.resume();
}
if (this.isPaused) {
this.resume();
}
};
p.setStateTime = function (state, time) {
if (time !== undefined) {
if (state) {
state.setTime(time);
state.sample();
}
}
else {
time = state;
let array = this._anims.array;
for (let i = 0; i < array.length; ++i) {
let anim = array[i];
anim.setTime(time);
anim.sample();
}
}
};
p.onStop = function () {
let iterator = this._anims;
let array = iterator.array;
for (iterator.i = 0; iterator.i < array.length; ++iterator.i) {
let anim = array[iterator.i];
anim.stop();
}
};
p.onPause = function () {
let array = this._anims.array;
for (let i = 0; i < array.length; ++i) {
let anim = array[i];
anim.pause();
// need to unbind animator to anim, or it maybe cannot be gc.
anim.animator = null;
}
};
p.onResume = function () {
let array = this._anims.array;
for (let i = 0; i < array.length; ++i) {
let anim = array[i];
// rebind animator to anim
anim.animator = this;
anim.resume();
}
};
p._reloadClip = function (state) {
initClipData(this.target, state);
};
// 这个方法应该是 SampledAnimCurve 才能用
function createBatchedProperty (propPath, firstDotIndex, mainValue, animValue) {
mainValue = mainValue.clone();
let nextValue = mainValue;
let leftIndex = firstDotIndex + 1;
let rightIndex = propPath.indexOf('.', leftIndex);
// scan property path
while (rightIndex !== -1) {
let nextName = propPath.slice(leftIndex, rightIndex);
nextValue = nextValue[nextName];
leftIndex = rightIndex + 1;
rightIndex = propPath.indexOf('.', leftIndex);
}
let lastPropName = propPath.slice(leftIndex);
nextValue[lastPropName] = animValue;
return mainValue;
}
if (CC_TEST) {
cc._Test.createBatchedProperty = createBatchedProperty;
}
function initClipData (root, state) {
let clip = state.clip;
state.duration = clip.duration;
state.speed = clip.speed;
state.wrapMode = clip.wrapMode;
state.frameRate = clip.sample;
if ((state.wrapMode & WrapModeMask.Loop) === WrapModeMask.Loop) {
state.repeatCount = Infinity;
}
else {
state.repeatCount = 1;
}
let curves = state.curves = clip.createCurves(state, root);
// events curve
let events = clip.events;
if (!CC_EDITOR && events) {
let curve;
for (let i = 0, l = events.length; i < l; i++) {
if (!curve) {
curve = new EventAnimCurve();
curve.target = root;
curves.push(curve);
}
let eventData = events[i];
let ratio = eventData.frame / state.duration;
let eventInfo;
let index = binarySearch(curve.ratios, ratio);
if (index >= 0) {
eventInfo = curve.events[index];
}
else {
eventInfo = new EventInfo();
curve.ratios.push(ratio);
curve.events.push(eventInfo);
}
eventInfo.add(eventData.func, eventData.params);
}
}
}
if (CC_TEST) {
cc._Test.initClipData = initClipData;
}
module.exports = AnimationAnimator;

View File

@@ -0,0 +1,308 @@
/****************************************************************************
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
const WrapMode = require('./types').WrapMode;
const { DynamicAnimCurve, quickFindIndex } = require('./animation-curves');
const sampleMotionPaths = require('./motion-path-helper').sampleMotionPaths;
const binarySearch = require('../core/utils/binary-search').binarySearchEpsilon;
/**
* !#en Class for animation data handling.
* !#zh 动画剪辑,用于存储动画数据。
* @class AnimationClip
* @extends Asset
*/
var AnimationClip = cc.Class({
name: 'cc.AnimationClip',
extends: cc.Asset,
properties: {
_duration: {
default: 0,
type: cc.Float,
},
/**
* !#en Duration of this animation.
* !#zh 动画的持续时间。
* @property duration
* @type {Number}
*/
duration: {
get: function () { return this._duration; },
},
/**
* !#en FrameRate of this animation.
* !#zh 动画的帧速率。
* @property sample
* @type {Number}
*/
sample: {
default: 60,
},
/**
* !#en Speed of this animation.
* !#zh 动画的播放速度。
* @property speed
* @type {Number}
*/
speed: {
default: 1
},
/**
* !#en WrapMode of this animation.
* !#zh 动画的循环模式。
* @property wrapMode
* @type {WrapMode}
*/
wrapMode: {
default: WrapMode.Normal
},
/**
* !#en Curve data.
* !#zh 曲线数据。
* @property curveData
* @type {Object}
* @example {@link cocos2d/core/animation-clip/curve-data.js}
*/
curveData: {
default: {},
visible: false,
},
/**
* !#en Event data.
* !#zh 事件数据。
* @property events
* @type {Object[]}
* @example {@link cocos2d/core/animation-clip/event-data.js}
* @typescript events: {frame: number, func: string, params: string[]}[]
*/
events: {
default: [],
visible: false,
}
},
statics: {
/**
* !#en Crate clip with a set of sprite frames
* !#zh 使用一组序列帧图片来创建动画剪辑
* @method createWithSpriteFrames
* @param {[SpriteFrame]} spriteFrames
* @param {Number} sample
* @return {AnimationClip}
* @static
* @example
*
* var clip = cc.AnimationClip.createWithSpriteFrames(spriteFrames, 10);
*
*/
createWithSpriteFrames: function (spriteFrames, sample) {
if (!Array.isArray(spriteFrames)) {
cc.errorID(3905);
return null;
}
var clip = new AnimationClip();
clip.sample = sample || clip.sample;
clip._duration = spriteFrames.length / clip.sample;
var frames = [];
var step = 1 / clip.sample;
for (var i = 0, l = spriteFrames.length; i < l; i++) {
frames[i] = { frame: (i * step), value: spriteFrames[i] };
}
clip.curveData = {
comps: {
// component
'cc.Sprite': {
// component properties
'spriteFrame': frames
}
}
};
return clip;
}
},
onLoad () {
this._duration = Number.parseFloat(this.duration);
this.speed = Number.parseFloat(this.speed);
this.wrapMode = Number.parseInt(this.wrapMode);
this.frameRate = Number.parseFloat(this.sample);
},
createPropCurve (target, propPath, keyframes) {
let motionPaths = [];
let isMotionPathProp = target instanceof cc.Node && propPath === 'position';
let curve = new DynamicAnimCurve();
// 缓存目标对象,所以 Component 必须一开始都创建好并且不能运行时动态替换……
curve.target = target;
curve.prop = propPath;
// for each keyframes
for (let i = 0, l = keyframes.length; i < l; i++) {
let keyframe = keyframes[i];
let ratio = keyframe.frame / this.duration;
curve.ratios.push(ratio);
if (isMotionPathProp) {
motionPaths.push(keyframe.motionPath);
}
let curveValue = keyframe.value;
curve.values.push(curveValue);
let curveTypes = keyframe.curve;
if (curveTypes) {
if (typeof curveTypes === 'string') {
curve.types.push(curveTypes);
continue;
}
else if (Array.isArray(curveTypes)) {
if (curveTypes[0] === curveTypes[1] &&
curveTypes[2] === curveTypes[3]) {
curve.types.push(DynamicAnimCurve.Linear);
}
else {
curve.types.push(DynamicAnimCurve.Bezier(curveTypes));
}
continue;
}
}
curve.types.push(DynamicAnimCurve.Linear);
}
if (isMotionPathProp) {
sampleMotionPaths(motionPaths, curve, this.duration, this.sample, target);
}
// if every piece of ratios are the same, we can use the quick function to find frame index.
let ratios = curve.ratios;
let currRatioDif, lastRatioDif;
let canOptimize = true;
let EPSILON = 1e-6;
for (let i = 1, l = ratios.length; i < l; i++) {
currRatioDif = ratios[i] - ratios[i-1];
if (i === 1) {
lastRatioDif = currRatioDif;
}
else if (Math.abs(currRatioDif - lastRatioDif) > EPSILON) {
canOptimize = false;
break;
}
}
curve._findFrameIndex = canOptimize ? quickFindIndex : binarySearch;
// find the lerp function
let firstValue = curve.values[0];
if (firstValue !== undefined && firstValue !== null && !curve._lerp) {
if (typeof firstValue === 'number') {
curve._lerp = DynamicAnimCurve.prototype._lerpNumber;
}
else if (firstValue instanceof cc.Quat) {
curve._lerp = DynamicAnimCurve.prototype._lerpQuat;
}
else if (firstValue instanceof cc.Vec2) {
curve._lerp = DynamicAnimCurve.prototype._lerpVector2;
}
else if (firstValue instanceof cc.Vec3) {
curve._lerp = DynamicAnimCurve.prototype._lerpVector3;
}
else if (firstValue.lerp) {
curve._lerp = DynamicAnimCurve.prototype._lerpObject;
}
}
return curve;
},
createTargetCurves (target, curveData, curves) {
let propsData = curveData.props;
let compsData = curveData.comps;
if (propsData) {
for (let propPath in propsData) {
let data = propsData[propPath];
let curve = this.createPropCurve(target, propPath, data);
curves.push(curve);
}
}
if (compsData) {
for (let compName in compsData) {
let comp = target.getComponent(compName);
if (!comp) {
continue;
}
let compData = compsData[compName];
for (let propPath in compData) {
let data = compData[propPath];
let curve = this.createPropCurve(comp, propPath, data);
curves.push(curve);
}
}
}
},
createCurves (state, root) {
let curveData = this.curveData;
let childrenCurveDatas = curveData.paths;
let curves = [];
this.createTargetCurves(root, curveData, curves);
for (let namePath in childrenCurveDatas) {
let target = cc.find(namePath, root);
if (!target) {
continue;
}
let childCurveDatas = childrenCurveDatas[namePath];
this.createTargetCurves(target, childCurveDatas, curves);
}
return curves;
}
});
cc.AnimationClip = module.exports = AnimationClip;

View File

@@ -0,0 +1,464 @@
/****************************************************************************
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
const bezierByTime = require('./bezier').bezierByTime;
const binarySearch = require('../core/utils/binary-search').binarySearchEpsilon;
const WrapModeMask = require('./types').WrapModeMask;
const WrappedInfo = require('./types').WrappedInfo;
/**
* Compute a new ratio by curve type
* @param {Number} ratio - The origin ratio
* @param {Array|String} type - If it's Array, then ratio will be computed with bezierByTime. If it's string, then ratio will be computed with cc.easing function
*/
function computeRatioByType (ratio, type) {
if (typeof type === 'string') {
var func = cc.easing[type];
if (func) {
ratio = func(ratio);
}
else {
cc.errorID(3906, type);
}
}
else if (Array.isArray(type)) {
// bezier curve
ratio = bezierByTime(type, ratio);
}
return ratio;
}
//
// 动画数据类,相当于 AnimationClip。
// 虽然叫做 AnimCurve但除了曲线可以保存任何类型的值。
//
// @class AnimCurve
//
//
var AnimCurve = cc.Class({
name: 'cc.AnimCurve',
//
// @method sample
// @param {number} time
// @param {number} ratio - The normalized time specified as a number between 0.0 and 1.0 inclusive.
// @param {AnimationState} state
//
sample: function (time, ratio, state) {},
onTimeChangedManually: undefined
});
/**
* 当每两帧之前的间隔都一样的时候可以使用此函数快速查找 index
*/
function quickFindIndex (ratios, ratio) {
var length = ratios.length - 1;
if (length === 0) return 0;
var start = ratios[0];
if (ratio < start) return 0;
var end = ratios[length];
if (ratio > end) return ~ratios.length;
ratio = (ratio - start) / (end - start);
var eachLength = 1 / length;
var index = ratio / eachLength;
var floorIndex = index | 0;
var EPSILON = 1e-6;
if ((index - floorIndex) < EPSILON) {
return floorIndex;
}
else if ((floorIndex + 1 - index) < EPSILON) {
return floorIndex + 1;
}
return ~(floorIndex + 1);
}
//
//
// @class DynamicAnimCurve
//
// @extends AnimCurve
//
var DynamicAnimCurve = cc.Class({
name: 'cc.DynamicAnimCurve',
extends: AnimCurve,
ctor () {
// cache last frame index
this._cachedIndex = 0;
},
properties: {
// The object being animated.
// @property target
// @type {object}
target: null,
// The name of the property being animated.
// @property prop
// @type {string}
prop: '',
// The values of the keyframes. (y)
// @property values
// @type {any[]}
values: [],
// The keyframe ratio of the keyframe specified as a number between 0.0 and 1.0 inclusive. (x)
// @property ratios
// @type {number[]}
ratios: [],
// @property types
// @param {object[]}
// Each array item maybe type:
// - [x, x, x, x]: Four control points for bezier
// - null: linear
types: [],
},
_findFrameIndex: binarySearch,
_lerp: undefined,
_lerpNumber (from, to, t) {
return from + (to - from) * t;
},
_lerpObject (from, to, t) {
return from.lerp(to, t);
},
_lerpQuat: (function () {
let out = cc.quat();
return function (from, to, t) {
return from.lerp(to, t, out);
};
})(),
_lerpVector2: (function () {
let out = cc.v2();
return function (from, to, t) {
return from.lerp(to, t, out);
};
})(),
_lerpVector3: (function () {
let out = cc.v3();
return function (from, to, t) {
return from.lerp(to, t, out);
};
})(),
sample (time, ratio, state) {
let values = this.values;
let ratios = this.ratios;
let frameCount = ratios.length;
if (frameCount === 0) {
return;
}
// only need to refind frame index when ratio is out of range of last from ratio and to ratio.
let shoudRefind = true;
let cachedIndex = this._cachedIndex;
if (cachedIndex < 0) {
cachedIndex = ~cachedIndex;
if (cachedIndex > 0 && cachedIndex < ratios.length) {
let fromRatio = ratios[cachedIndex - 1];
let toRatio = ratios[cachedIndex];
if (ratio > fromRatio && ratio < toRatio) {
shoudRefind = false;
}
}
}
if (shoudRefind) {
this._cachedIndex = this._findFrameIndex(ratios, ratio);
}
// evaluate value
let value;
let index = this._cachedIndex;
if (index < 0) {
index = ~index;
if (index <= 0) {
value = values[0];
}
else if (index >= frameCount) {
value = values[frameCount - 1];
}
else {
var fromVal = values[index - 1];
if (!this._lerp) {
value = fromVal;
}
else {
var fromRatio = ratios[index - 1];
var toRatio = ratios[index];
var type = this.types[index - 1];
var ratioBetweenFrames = (ratio - fromRatio) / (toRatio - fromRatio);
if (type) {
ratioBetweenFrames = computeRatioByType(ratioBetweenFrames, type);
}
// calculate value
var toVal = values[index];
value = this._lerp(fromVal, toVal, ratioBetweenFrames);
}
}
}
else {
value = values[index];
}
this.target[this.prop] = value;
}
});
DynamicAnimCurve.Linear = null;
DynamicAnimCurve.Bezier = function (controlPoints) {
return controlPoints;
};
/**
* Event information,
* @class EventInfo
*
*/
var EventInfo = function () {
this.events = [];
};
/**
* @param {Function} [func] event function
* @param {Object[]} [params] event params
*/
EventInfo.prototype.add = function (func, params) {
this.events.push({
func: func || '',
params: params || []
});
};
/**
*
* @class EventAnimCurve
*
* @extends AnimCurve
*/
var EventAnimCurve = cc.Class({
name: 'cc.EventAnimCurve',
extends: AnimCurve,
properties: {
/**
* The object being animated.
* @property target
* @type {object}
*/
target: null,
/** The keyframe ratio of the keyframe specified as a number between 0.0 and 1.0 inclusive. (x)
* @property ratios
* @type {number[]}
*/
ratios: [],
/**
* @property events
* @type {EventInfo[]}
*/
events: [],
_wrappedInfo: {
default: function () {
return new WrappedInfo();
}
},
_lastWrappedInfo: null,
_ignoreIndex: NaN
},
_wrapIterations: function (iterations) {
if (iterations - (iterations | 0) === 0) iterations -= 1;
return iterations | 0;
},
sample: function (time, ratio, state) {
var length = this.ratios.length;
var currentWrappedInfo = state.getWrappedInfo(state.time, this._wrappedInfo);
var direction = currentWrappedInfo.direction;
var currentIndex = binarySearch(this.ratios, currentWrappedInfo.ratio);
if (currentIndex < 0) {
currentIndex = ~currentIndex - 1;
// if direction is inverse, then increase index
if (direction < 0) currentIndex += 1;
}
if (this._ignoreIndex !== currentIndex) {
this._ignoreIndex = NaN;
}
currentWrappedInfo.frameIndex = currentIndex;
if (!this._lastWrappedInfo) {
this._fireEvent(currentIndex);
this._lastWrappedInfo = new WrappedInfo(currentWrappedInfo);
return;
}
var wrapMode = state.wrapMode;
var currentIterations = this._wrapIterations(currentWrappedInfo.iterations);
var lastWrappedInfo = this._lastWrappedInfo;
var lastIterations = this._wrapIterations(lastWrappedInfo.iterations);
var lastIndex = lastWrappedInfo.frameIndex;
var lastDirection = lastWrappedInfo.direction;
var interationsChanged = lastIterations !== -1 && currentIterations !== lastIterations;
if (lastIndex === currentIndex && interationsChanged && length === 1) {
this._fireEvent(0);
}
else if (lastIndex !== currentIndex || interationsChanged) {
direction = lastDirection;
do {
if (lastIndex !== currentIndex) {
if (direction === -1 && lastIndex === 0 && currentIndex > 0) {
if ((wrapMode & WrapModeMask.PingPong) === WrapModeMask.PingPong) {
direction *= -1;
}
else {
lastIndex = length;
}
lastIterations ++;
}
else if (direction === 1 && lastIndex === length - 1 && currentIndex < length - 1) {
if ((wrapMode & WrapModeMask.PingPong) === WrapModeMask.PingPong) {
direction *= -1;
}
else {
lastIndex = -1;
}
lastIterations ++;
}
if (lastIndex === currentIndex) break;
if (lastIterations > currentIterations) break;
}
lastIndex += direction;
cc.director.getAnimationManager().pushDelayEvent(this, '_fireEvent', [lastIndex]);
} while (lastIndex !== currentIndex && lastIndex > -1 && lastIndex < length);
}
this._lastWrappedInfo.set(currentWrappedInfo);
},
_fireEvent: function (index) {
if (index < 0 || index >= this.events.length || this._ignoreIndex === index) return;
var eventInfo = this.events[index];
var events = eventInfo.events;
if ( !this.target.isValid ) {
return;
}
var components = this.target._components;
for (var i = 0; i < events.length; i++) {
var event = events[i];
var funcName = event.func;
for (var j = 0; j < components.length; j++) {
var component = components[j];
var func = component[funcName];
if (func) func.apply(component, event.params);
}
}
},
onTimeChangedManually: function (time, state) {
this._lastWrappedInfo = null;
this._ignoreIndex = NaN;
var info = state.getWrappedInfo(time, this._wrappedInfo);
var direction = info.direction;
var frameIndex = binarySearch(this.ratios, info.ratio);
// only ignore when time not on a frame index
if (frameIndex < 0) {
frameIndex = ~frameIndex - 1;
// if direction is inverse, then increase index
if (direction < 0) frameIndex += 1;
this._ignoreIndex = frameIndex;
}
}
});
if (CC_TEST) {
cc._Test.DynamicAnimCurve = DynamicAnimCurve;
cc._Test.EventAnimCurve = EventAnimCurve;
cc._Test.quickFindIndex = quickFindIndex;
}
module.exports = {
AnimCurve: AnimCurve,
DynamicAnimCurve: DynamicAnimCurve,
EventAnimCurve: EventAnimCurve,
EventInfo: EventInfo,
computeRatioByType: computeRatioByType,
quickFindIndex: quickFindIndex
};

View File

@@ -0,0 +1,93 @@
/****************************************************************************
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
var js = cc.js;
var AnimationManager = cc.Class({
ctor: function () {
this._anims = new js.array.MutableForwardIterator([]);
this._delayEvents = [];
cc.director._scheduler && cc.director._scheduler.enableForTarget(this);
},
// for manager
update: function (dt) {
var iterator = this._anims;
var array = iterator.array;
for (iterator.i = 0; iterator.i < array.length; ++iterator.i) {
var anim = array[iterator.i];
if (anim._isPlaying && !anim._isPaused) {
anim.update(dt);
}
}
var events = this._delayEvents;
for (let i = 0; i < events.length; i++) {
var event = events[i];
event.target[event.func].apply(event.target, event.args);
}
events.length = 0;
},
destruct: function () {},
/**
* @param {AnimationState} anim
*/
addAnimation: function (anim) {
var index = this._anims.array.indexOf(anim);
if (index === -1) {
this._anims.push(anim);
}
},
/**
* @param {AnimationState} anim
*/
removeAnimation: function (anim) {
var index = this._anims.array.indexOf(anim);
if (index >= 0) {
this._anims.fastRemoveAt(index);
}
else {
cc.errorID(3907);
}
},
pushDelayEvent: function (target, func, args) {
this._delayEvents.push({
target: target,
func: func,
args: args
});
}
});
cc.AnimationManager = module.exports = AnimationManager;

View File

@@ -0,0 +1,523 @@
/****************************************************************************
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
var js = cc.js;
var Playable = require('./playable');
var Types = require('./types');
var WrappedInfo = Types.WrappedInfo;
var WrapMode = Types.WrapMode;
var WrapModeMask = Types.WrapModeMask;
/**
* !#en
* The AnimationState gives full control over animation playback process.
* In most cases the Animation Component is sufficient and easier to use. Use the AnimationState if you need full control.
* !#zh
* AnimationState 完全控制动画播放过程。<br/>
* 大多数情况下 动画组件 是足够和易于使用的。如果您需要更多的动画控制接口,请使用 AnimationState。
* @class AnimationState
* @extends Playable
*
*/
/**
* @method constructor
* @param {AnimationClip} clip
* @param {String} [name]
*/
function AnimationState (clip, name) {
Playable.call(this);
// Mark whether the current frame is played.
// When set new time to animation state, we should ensure the frame at the specified time being played at next update.
this._currentFramePlayed = false;
this._delay = 0;
this._delayTime = 0;
this._wrappedInfo = new WrappedInfo();
this._lastWrappedInfo = null;
this._process = process;
this._clip = clip;
this._name = name || (clip && clip.name);
/**
* @property animator
* @type {AnimationAnimator}
* @private
*/
this.animator = null;
/**
* !#en The curves list.
* !#zh 曲线列表。
* @property curves
* @type {Object[]}
*/
this.curves = [];
// http://www.w3.org/TR/web-animations/#idl-def-AnimationTiming
/**
* !#en The start delay which represents the number of seconds from an animation's start time to the start of
* the active interval.
* !#zh 延迟多少秒播放。
*
* @property delay
* @type {Number}
* @default 0
*/
this.delay = 0;
/**
* !#en The animation's iteration count property.
*
* A real number greater than or equal to zero (including positive infinity) representing the number of times
* to repeat the animation node.
*
* Values less than zero and NaN values are treated as the value 1.0 for the purpose of timing model
* calculations.
*
* !#zh 迭代次数,指动画播放多少次后结束, normalize time。 如 2.52次半
*
* @property repeatCount
* @type {Number}
* @default 1
*/
this.repeatCount = 1;
/**
* !#en The iteration duration of this animation in seconds. (length)
* !#zh 单次动画的持续时间,秒。
*
* @property duration
* @type {Number}
* @readOnly
*/
this.duration = 1;
/**
* !#en The animation's playback speed. 1 is normal playback speed.
* !#zh 播放速率。
* @property speed
* @type {Number}
* @default: 1.0
*/
this.speed = 1;
/**
* !#en
* Wrapping mode of the playing animation.
* Notice : dynamic change wrapMode will reset time and repeatCount property
* !#zh
* 动画循环方式。
* 需要注意的是,动态修改 wrapMode 时,会重置 time 以及 repeatCount
*
* @property wrapMode
* @type {WrapMode}
* @default: WrapMode.Normal
*/
this.wrapMode = WrapMode.Normal;
/**
* !#en The current time of this animation in seconds.
* !#zh 动画当前的时间,秒。
* @property time
* @type {Number}
* @default 0
*/
this.time = 0;
// Animation as event target
this._target = null;
this._lastframeEventOn = false;
this.emit = function () {
var args = new Array(arguments.length);
for (var i = 0, l = args.length; i < l; i++) {
args[i] = arguments[i];
}
cc.director.getAnimationManager().pushDelayEvent(this, '_emit', args);
};
}
js.extend(AnimationState, Playable);
var proto = AnimationState.prototype;
proto._emit = function (type, state) {
if (this._target && this._target.isValid) {
this._target.emit(type, type, state);
}
};
proto.on = function (type, callback, target) {
if (this._target && this._target.isValid) {
if (type === 'lastframe') {
this._lastframeEventOn = true;
}
return this._target.on(type, callback, target);
}
else {
return null;
}
};
proto.once = function (type, callback, target) {
if (this._target && this._target.isValid) {
if (type === 'lastframe') {
this._lastframeEventOn = true;
}
let self = this;
return this._target.once(type, function (event) {
callback.call(target, event);
self._lastframeEventOn = false;
});
}
else {
return null;
}
};
proto.off = function (type, callback, target) {
if (this._target && this._target.isValid) {
if (type === 'lastframe') {
if (!this._target.hasEventListener(type)) {
this._lastframeEventOn = false;
}
}
this._target.off(type, callback, target);
}
};
proto._setEventTarget = function (target) {
this._target = target;
};
proto.onPlay = function () {
// replay
this.setTime(0);
this._delayTime = this._delay;
cc.director.getAnimationManager().addAnimation(this);
if (this.animator) {
this.animator.addAnimation(this);
}
this.emit('play', this);
};
proto.onStop = function () {
if (!this.isPaused) {
cc.director.getAnimationManager().removeAnimation(this);
}
if (this.animator) {
this.animator.removeAnimation(this);
}
this.emit('stop', this);
};
proto.onResume = function () {
cc.director.getAnimationManager().addAnimation(this);
this.emit('resume', this);
};
proto.onPause = function () {
cc.director.getAnimationManager().removeAnimation(this);
this.emit('pause', this);
};
proto.setTime = function (time) {
this._currentFramePlayed = false;
this.time = time || 0;
var curves = this.curves;
for (var i = 0, l = curves.length; i < l; i++) {
var curve = curves[i];
if (curve.onTimeChangedManually) {
curve.onTimeChangedManually(time, this);
}
}
};
function process () {
// sample
var info = this.sample();
if (this._lastframeEventOn) {
var lastInfo;
if (!this._lastWrappedInfo) {
lastInfo = this._lastWrappedInfo = new WrappedInfo(info);
} else {
lastInfo = this._lastWrappedInfo;
}
if (this.repeatCount > 1 && ((info.iterations | 0) > (lastInfo.iterations | 0))) {
this.emit('lastframe', this);
}
lastInfo.set(info);
}
if (info.stopped) {
this.stop();
this.emit('finished', this);
}
}
function simpleProcess () {
var time = this.time;
var duration = this.duration;
if (time > duration) {
time = time % duration;
if (time === 0) time = duration;
}
else if (time < 0) {
time = time % duration;
if (time !== 0) time += duration;
}
var ratio = time / duration;
var curves = this.curves;
for (var i = 0, len = curves.length; i < len; i++) {
var curve = curves[i];
curve.sample(time, ratio, this);
}
if (this._lastframeEventOn) {
if (this._lastIterations === undefined) {
this._lastIterations = ratio;
}
if ((this.time > 0 && this._lastIterations > ratio) || (this.time < 0 && this._lastIterations < ratio)) {
this.emit('lastframe', this);
}
this._lastIterations = ratio;
}
}
proto.update = function (delta) {
// calculate delay time
if (this._delayTime > 0) {
this._delayTime -= delta;
if (this._delayTime > 0) {
// still waiting
return;
}
}
// make first frame perfect
//var playPerfectFirstFrame = (this.time === 0);
if (this._currentFramePlayed) {
this.time += (delta * this.speed);
}
else {
this._currentFramePlayed = true;
}
this._process();
};
proto._needRevers = function (currentIterations) {
var wrapMode = this.wrapMode;
var needRevers = false;
if ((wrapMode & WrapModeMask.PingPong) === WrapModeMask.PingPong) {
var isEnd = currentIterations - (currentIterations | 0) === 0;
if (isEnd && (currentIterations > 0)) {
currentIterations -= 1;
}
var isOddIteration = currentIterations & 1;
if (isOddIteration) {
needRevers = !needRevers;
}
}
if ((wrapMode & WrapModeMask.Reverse) === WrapModeMask.Reverse) {
needRevers = !needRevers;
}
return needRevers;
};
proto.getWrappedInfo = function (time, info) {
info = info || new WrappedInfo();
var stopped = false;
var duration = this.duration;
var repeatCount = this.repeatCount;
var currentIterations = time > 0 ? (time / duration) : -(time / duration);
if (currentIterations >= repeatCount) {
currentIterations = repeatCount;
stopped = true;
var tempRatio = repeatCount - (repeatCount | 0);
if (tempRatio === 0) {
tempRatio = 1; // 如果播放过,动画不复位
}
time = tempRatio * duration * (time > 0 ? 1 : -1);
}
if (time > duration) {
var tempTime = time % duration;
time = tempTime === 0 ? duration : tempTime;
}
else if (time < 0) {
time = time % duration;
if (time !== 0 ) time += duration;
}
var needRevers = false;
var shouldWrap = this._wrapMode & WrapModeMask.ShouldWrap;
if (shouldWrap) {
needRevers = this._needRevers(currentIterations);
}
var direction = needRevers ? -1 : 1;
if (this.speed < 0) {
direction *= -1;
}
// calculate wrapped time
if (shouldWrap && needRevers) {
time = duration - time;
}
info.ratio = time / duration;
info.time = time;
info.direction = direction;
info.stopped = stopped;
info.iterations = currentIterations;
return info;
};
proto.sample = function () {
var info = this.getWrappedInfo(this.time, this._wrappedInfo);
var curves = this.curves;
for (var i = 0, len = curves.length; i < len; i++) {
var curve = curves[i];
curve.sample(info.time, info.ratio, this);
}
return info;
};
/**
* !#en The clip that is being played by this animation state.
* !#zh 此动画状态正在播放的剪辑。
* @property clip
* @type {AnimationClip}
* @final
*/
js.get(proto, 'clip', function () {
return this._clip;
});
/**
* !#en The name of the playing animation.
* !#zh 动画的名字
* @property name
* @type {String}
* @readOnly
*/
js.get(proto, 'name', function () {
return this._name;
});
js.obsolete(proto, 'AnimationState.length', 'duration');
js.getset(proto, 'curveLoaded',
function () {
return this.curves.length > 0;
},
function () {
this.curves.length = 0;
}
);
js.getset(proto, 'wrapMode',
function () {
return this._wrapMode;
},
function (value) {
this._wrapMode = value;
if (CC_EDITOR) return;
// dynamic change wrapMode will need reset time to 0
this.time = 0;
if (value & WrapModeMask.Loop) {
this.repeatCount = Infinity;
}
else {
this.repeatCount = 1;
}
}
);
js.getset(proto, 'repeatCount',
function () {
return this._repeatCount;
},
function (value) {
this._repeatCount = value;
var shouldWrap = this._wrapMode & WrapModeMask.ShouldWrap;
var reverse = (this.wrapMode & WrapModeMask.Reverse) === WrapModeMask.Reverse;
if (value === Infinity && !shouldWrap && !reverse) {
this._process = simpleProcess;
}
else {
this._process = process;
}
}
);
js.getset(proto, 'delay',
function () {
return this._delay;
},
function (value) {
this._delayTime = this._delay = value;
}
);
cc.AnimationState = module.exports = AnimationState;

View File

@@ -0,0 +1,213 @@
/****************************************************************************
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
//var bezier = (function () {
// function B1 (t) { return (t * t * t); }
// function B2 (t) { return (3 * t * t * (1 - t)); }
// function B3 (t) { return (3 * t * (1 - t) * (1 - t)); }
// function B4 (t) { return ((1 - t) * (1 - t) * (1 - t)); }
// function bezier (C1, C2, C3, C4, t) {
// return C1 * B1(t) + C2 * B2(t) + C3 * B3(t) + C4 * B4(t);
// }
//
// //function bezier (C1, C2, C3, C4, t, out) {
// // out.x = C1.x * B1(t) + C2.x * B2(t) + C3.x * B3(t) + C4.x * B4(t);
// // out.y = C1.y * B1(t) + C2.y * B2(t) + C3.y * B3(t) + C4.y * B4(t);
// //}
//
// return bezier;
//})();
function bezier (C1, C2, C3, C4, t) {
var t1 = 1 - t;
return t1 * (t1 * (C1 + (C2 * 3 - C1) * t) + C3 * 3 * t * t) + C4 * t * t * t;
}
//function bezier (c0, c1, c2, c3, t) {
// var cy = 3.0 * (c1);
// var by = 3.0 * (c3 - c1) - cy;
// var ay = 1 - cy - by;
// return (ay * t * t * t) + (by * t * t) + (cy * t);
//}
//var sin = Math.sin;
var cos = Math.cos,
acos = Math.acos,
max = Math.max,
//atan2 = Math.atan2,
pi = Math.PI,
tau = 2 * pi,
sqrt = Math.sqrt;
function crt (v) {
if (v < 0) {
return -Math.pow(-v, 1 / 3);
}
else {
return Math.pow(v, 1 / 3);
}
}
//function align (curve, line) {
// var tx = line.p1.x,
// ty = line.p1.y,
// a = -atan2(line.p2.y-ty, line.p2.x-tx);
// curve = [{x:0, y:1}, {x: curve[0], y: 1-curve[1]}, {x: curve[2], y: 1-curve[3]}, {x:1, y:0}];
// return curve.map(function(v) {
// return {
// x: (v.x-tx)*cos(a) - (v.y-ty)*sin(a),
// y: (v.x-tx)*sin(a) + (v.y-ty)*cos(a)
// };
// });
//}
// Modified from http://jsbin.com/yibipofeqi/1/edit, optimized for animations.
// The origin Cardano's algorithm is based on http://www.trans4mind.com/personal_development/mathematics/polynomials/cubicAlgebra.htm
function cardano (curve, x) {
// align curve with the intersecting line:
//var line = {p1: {x: x, y: 0}, p2: {x: x, y: 1}};
//var aligned = align(curve, line);
//// and rewrite from [a(1-t)^3 + 3bt(1-t)^2 + 3c(1-t)t^2 + dt^3] form
// pa = aligned[0].y,
// pb = aligned[1].y,
// pc = aligned[2].y,
// pd = aligned[3].y;
////// curve = [{x:0, y:1}, {x: curve[0], y: 1-curve[1]}, {x: curve[2], y: 1-curve[3]}, {x:1, y:0}];
var pa = x - 0;
var pb = x - curve[0];
var pc = x - curve[2];
var pd = x - 1;
// to [t^3 + at^2 + bt + c] form:
var pa3 = pa * 3;
var pb3 = pb * 3;
var pc3 = pc * 3;
var d = (-pa + pb3 - pc3 + pd),
rd = 1 / d,
r3 = 1 / 3,
a = (pa3 - 6 * pb + pc3) * rd,
a3 = a * r3,
b = (-pa3 + pb3) * rd,
c = pa * rd,
// then, determine p and q:
p = (3 * b - a * a) * r3,
p3 = p * r3,
q = (2 * a * a * a - 9 * a * b + 27 * c) / 27,
q2 = q / 2,
// and determine the discriminant:
discriminant = q2 * q2 + p3 * p3 * p3,
// and some reserved variables
u1, v1, x1, x2, x3;
// If the discriminant is negative, use polar coordinates
// to get around square roots of negative numbers
if (discriminant < 0) {
var mp3 = -p * r3,
mp33 = mp3 * mp3 * mp3,
r = sqrt(mp33),
// compute cosphi corrected for IEEE float rounding:
t = -q / (2 * r),
cosphi = t < -1 ? -1 : t > 1 ? 1 : t,
phi = acos(cosphi),
crtr = crt(r),
t1 = 2 * crtr;
x1 = t1 * cos(phi * r3) - a3;
x2 = t1 * cos((phi + tau) * r3) - a3;
x3 = t1 * cos((phi + 2 * tau) * r3) - a3;
// choose best percentage
if (0 <= x1 && x1 <= 1) {
if (0 <= x2 && x2 <= 1) {
if (0 <= x3 && x3 <= 1) {
return max(x1, x2, x3);
}
else {
return max(x1, x2);
}
}
else if (0 <= x3 && x3 <= 1) {
return max(x1, x3);
}
else {
return x1;
}
}
else {
if (0 <= x2 && x2 <= 1) {
if (0 <= x3 && x3 <= 1) {
return max(x2, x3);
}
else {
return x2;
}
}
else {
return x3;
}
}
}
else if (discriminant === 0) {
u1 = q2 < 0 ? crt(-q2) : -crt(q2);
x1 = 2 * u1 - a3;
x2 = -u1 - a3;
// choose best percentage
if (0 <= x1 && x1 <= 1) {
if (0 <= x2 && x2 <= 1) {
return max(x1, x2);
}
else {
return x1;
}
}
else {
return x2;
}
}
// one real root, and two imaginary roots
else {
var sd = sqrt(discriminant);
u1 = crt(-q2 + sd);
v1 = crt(q2 + sd);
x1 = u1 - v1 - a3;
return x1;
}
}
function bezierByTime (controlPoints, x) {
var percent = cardano(controlPoints, x); // t
var p1y = controlPoints[1]; // b
var p2y = controlPoints[3]; // c
// return bezier(0, p1y, p2y, 1, percent);
return ((1 - percent) * (p1y + (p2y - p1y) * percent) * 3 + percent * percent) * percent;
}
if (CC_TEST) {
cc._Test.bezier = bezier;
cc._Test.bezierByTime = bezierByTime;
}
module.exports = {
bezier: bezier,
bezierByTime: bezierByTime
};

View File

@@ -0,0 +1,538 @@
/****************************************************************************
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
/**
* @module cc
*/
/**
* !#en
* This class provide easing methods for {{#crossLink "tween"}}{{/crossLink}} class.<br>
* Demonstratio: https://easings.net/
* !#zh
* 缓动函数类,为 {{#crossLink "Tween"}}{{/crossLink}} 提供缓动效果函数。<br>
* 函数效果演示: https://easings.net/
* @class Easing
*/
var easing = {
constant: function () { return 0; },
linear: function (k) { return k; },
// quad
// easing equation function for a quadratic (t^2)
// @param t: Current time (in frames or seconds).
// @return: The correct value.
/**
* !#en Easing in with quadratic formula. From slow to fast.
* !#zh 平方曲线缓入函数。运动由慢到快。
* @method quadIn
* @param {Number} t The current time as a percentage of the total time.
* @return {Number} The correct value
*/
quadIn: function (k) { return k * k; },
/**
* !#en Easing out with quadratic formula. From fast to slow.
* !#zh 平方曲线缓出函数。运动由快到慢。
* @method quadOut
* @param {Number} t The current time as a percentage of the total time.
* @return {Number} The correct value
*/
quadOut: function (k) { return k * ( 2 - k ); },
/**
* !#en Easing in and out with quadratic formula. From slow to fast, then back to slow.
* !#zh 平方曲线缓入缓出函数。运动由慢到快再到慢。
* @method quadInOut
* @param {Number} t The current time as a percentage of the total time.
* @return {Number} The correct value
*/
quadInOut: function (k) {
if (( k *= 2 ) < 1) {
return 0.5 * k * k;
}
return -0.5 * ( --k * ( k - 2 ) - 1 );
},
// cubic
// easing equation function for a cubic (t^3)
// @param t: Current time (in frames or seconds).
// @return: The correct value.
/**
* !#en Easing in with cubic formula. From slow to fast.
* !#zh 立方曲线缓入函数。运动由慢到快。
* @method cubicIn
* @param {Number} t The current time as a percentage of the total time.
* @return {Number} The correct value.
*/
cubicIn: function (k) { return k * k * k; },
/**
* !#en Easing out with cubic formula. From slow to fast.
* !#zh 立方曲线缓出函数。运动由快到慢。
* @method cubicOut
* @param {Number} t The current time as a percentage of the total time.
* @return {Number} The correct value.
*/
cubicOut: function (k) { return --k * k * k + 1; },
/**
* !#en Easing in and out with cubic formula. From slow to fast, then back to slow.
* !#zh 立方曲线缓入缓出函数。运动由慢到快再到慢。
* @method cubicInOut
* @param {Number} t The current time as a percentage of the total time.
* @return {Number} The correct value.
*/
cubicInOut: function (k) {
if (( k *= 2 ) < 1) {
return 0.5 * k * k * k;
}
return 0.5 * ( ( k -= 2 ) * k * k + 2 );
},
// quart
// easing equation function for a quartic (t^4)
// @param t: Current time (in frames or seconds).
// @return: The correct value.
/**
* !#en Easing in with quartic formula. From slow to fast.
* !#zh 四次方曲线缓入函数。运动由慢到快。
* @method quartIn
* @param {Number} t The current time as a percentage of the total time.
* @return {Number} The correct value.
*/
quartIn: function (k) { return k * k * k * k; },
/**
* !#en Easing out with quartic formula. From fast to slow.
* !#zh 四次方曲线缓出函数。运动由快到慢。
* @method quartOut
* @param {Number} t The current time as a percentage of the total time.
* @return {Number} The correct value.
*/
quartOut: function (k) { return 1 - ( --k * k * k * k ); },
/**
* !#en Easing in and out with quartic formula. From slow to fast, then back to slow.
* !#zh 四次方曲线缓入缓出函数。运动由慢到快再到慢。
* @method quartInOut
* @param {Number} t The current time as a percentage of the total time.
* @return {Number} The correct value.
*/
quartInOut: function (k) {
if (( k *= 2 ) < 1) {
return 0.5 * k * k * k * k;
}
return -0.5 * ( ( k -= 2 ) * k * k * k - 2 );
},
// quint
// easing equation function for a quintic (t^5)
// @param t: Current time (in frames or seconds).
// @return: The correct value.
/**
* !#en Easing in with quintic formula. From slow to fast.
* !#zh 五次方曲线缓入函数。运动由慢到快。
* @method quintIn
* @param {Number} t The current time as a percentage of the total time.
* @return {Number} The correct value.
*/
quintIn: function (k) { return k * k * k * k * k; },
/**
* !#en Easing out with quintic formula. From fast to slow.
* !#zh 五次方曲线缓出函数。运动由快到慢。
* @method quintOut
* @param {Number} t The current time as a percentage of the total time.
* @return {Number} The correct value.
*/
quintOut: function (k) { return --k * k * k * k * k + 1; },
/**
* !#en Easing in and out with quintic formula. From slow to fast, then back to slow.
* !#zh 五次方曲线缓入缓出函数。运动由慢到快再到慢。
* @method quintInOut
* @param {Number} t The current time as a percentage of the total time.
* @return {Number} The correct value.
*/
quintInOut: function (k) {
if (( k *= 2 ) < 1) {
return 0.5 * k * k * k * k * k;
}
return 0.5 * ( ( k -= 2 ) * k * k * k * k + 2 );
},
// sine
// easing equation function for a sinusoidal (sin(t))
// @param t: Current time (in frames or seconds).
// @return: The correct value.
/**
* !#en Easing in and out with sine formula. From slow to fast.
* !#zh 正弦曲线缓入函数。运动由慢到快。
* @method sineIn
* @param {Number} t The current time as a percentage of the total time.
* @return {Number} The correct value.
*/
sineIn: function (k) { return 1 - Math.cos(k * Math.PI / 2); },
/**
* !#en Easing in and out with sine formula. From fast to slow.
* !#zh 正弦曲线缓出函数。运动由快到慢。
* @method sineOut
* @param {Number} t The current time as a percentage of the total time.
* @return {Number} The correct value.
*/
sineOut: function (k) { return Math.sin(k * Math.PI / 2); },
/**
* !#en Easing in and out with sine formula. From slow to fast, then back to slow.
* !#zh 正弦曲线缓入缓出函数。运动由慢到快再到慢。
* @method sineInOut
* @param {Number} t The current time as a percentage of the total time.
* @return {Number} The correct value.
*/
sineInOut: function (k) { return 0.5 * ( 1 - Math.cos(Math.PI * k) ); },
// expo
// easing equation function for an exponential (2^t)
// param t: Current time (in frames or seconds).
// return: The correct value.
/**
* !#en Easing in and out with exponential formula. From slow to fast.
* !#zh 指数曲线缓入函数。运动由慢到快。
* @method expoIn
* @param {Number} t The current time as a percentage of the total time.
* @return {Number} The correct value.
*/
expoIn: function (k) { return k === 0 ? 0 : Math.pow(1024, k - 1); },
/**
* !#en Easing in and out with exponential formula. From fast to slow.
* !#zh 指数曲线缓出函数。运动由快到慢。
* @method expoOut
* @param {Number} t The current time as a percentage of the total time.
* @return {Number} The correct value.
*/
expoOut: function (k) { return k === 1 ? 1 : 1 - Math.pow(2, -10 * k); },
/**
* !#en Easing in and out with exponential formula. From slow to fast.
* !#zh 指数曲线缓入和缓出函数。运动由慢到很快再到慢。
* @method expoInOut
* @param {Number} t The current time as a percentage of the total time, then back to slow.
* @return {Number} The correct value.
*/
expoInOut: function (k) {
if (k === 0) {
return 0;
}
if (k === 1) {
return 1;
}
if (( k *= 2 ) < 1) {
return 0.5 * Math.pow(1024, k - 1);
}
return 0.5 * ( -Math.pow(2, -10 * ( k - 1 )) + 2 );
},
// circ
// easing equation function for a circular (sqrt(1-t^2))
// @param t: Current time (in frames or seconds).
// @return: The correct value.
/**
* !#en Easing in and out with circular formula. From slow to fast.
* !#zh 循环公式缓入函数。运动由慢到快。
* @method circIn
* @param {Number} t The current time as a percentage of the total time.
* @return {Number} The correct value.
*/
circIn: function (k) { return 1 - Math.sqrt(1 - k * k); },
/**
* !#en Easing in and out with circular formula. From fast to slow.
* !#zh 循环公式缓出函数。运动由快到慢。
* @method circOut
* @param {Number} t The current time as a percentage of the total time.
* @return {Number} The correct value.
*/
circOut: function (k) { return Math.sqrt(1 - ( --k * k )); },
/**
* !#en Easing in and out with circular formula. From slow to fast.
* !#zh 指数曲线缓入缓出函数。运动由慢到很快再到慢。
* @method circInOut
* @param {Number} t The current time as a percentage of the total time, then back to slow.
* @return {Number} The correct value.
*/
circInOut: function (k) {
if (( k *= 2 ) < 1) {
return -0.5 * ( Math.sqrt(1 - k * k) - 1);
}
return 0.5 * ( Math.sqrt(1 - ( k -= 2) * k) + 1);
},
// elastic
// easing equation function for an elastic (exponentially decaying sine wave)
// @param t: Current time (in frames or seconds).
// @return: The correct value.
// recommand value: elastic (t)
/**
* !#en Easing in action with a spring oscillating effect.
* !#zh 弹簧回震效果的缓入函数。
* @method elasticIn
* @param {Number} t The current time as a percentage of the total time.
* @return {Number} The correct value.
*/
elasticIn: function (k) {
var s, a = 0.1, p = 0.4;
if (k === 0) {
return 0;
}
if (k === 1) {
return 1;
}
if (!a || a < 1) {
a = 1;
s = p / 4;
}
else {
s = p * Math.asin(1 / a) / ( 2 * Math.PI );
}
return -( a * Math.pow(2, 10 * ( k -= 1 )) * Math.sin(( k - s ) * ( 2 * Math.PI ) / p) );
},
/**
* !#en Easing out action with a spring oscillating effect.
* !#zh 弹簧回震效果的缓出函数。
* @method elasticOut
* @param {Number} t The current time as a percentage of the total time.
* @return {Number} The correct value.
*/
elasticOut: function (k) {
var s, a = 0.1, p = 0.4;
if (k === 0) {
return 0;
}
if (k === 1) {
return 1;
}
if (!a || a < 1) {
a = 1;
s = p / 4;
}
else {
s = p * Math.asin(1 / a) / ( 2 * Math.PI );
}
return ( a * Math.pow(2, -10 * k) * Math.sin(( k - s ) * ( 2 * Math.PI ) / p) + 1 );
},
/**
* !#en Easing in and out action with a spring oscillating effect.
* !#zh 弹簧回震效果的缓入缓出函数。
* @method elasticInOut
* @param {Number} t The current time as a percentage of the total time.
* @return {Number} The correct value.
*/
elasticInOut: function (k) {
var s, a = 0.1, p = 0.4;
if (k === 0) {
return 0;
}
if (k === 1) {
return 1;
}
if (!a || a < 1) {
a = 1;
s = p / 4;
}
else {
s = p * Math.asin(1 / a) / ( 2 * Math.PI );
}
if (( k *= 2 ) < 1) {
return -0.5 *
( a * Math.pow(2, 10 * ( k -= 1 )) * Math.sin(( k - s ) * ( 2 * Math.PI ) / p) );
}
return a * Math.pow(2, -10 * ( k -= 1 )) * Math.sin(( k - s ) * ( 2 * Math.PI ) / p) * 0.5 + 1;
},
// back
// easing equation function for a back (overshooting cubic easing: (s+1)*t^3 - s*t^2)
// @param t: Current time (in frames or seconds).
// @return: The correct value.
/**
* !#en Easing in action with "back up" behavior.
* !#zh 回退效果的缓入函数。
* @method backIn
* @param {Number} t The current time as a percentage of the total time.
* @return {Number} The correct value.
*/
backIn: function (k) {
var s = 1.70158;
return k * k * ( ( s + 1 ) * k - s );
},
/**
* !#en Easing out action with "back up" behavior.
* !#zh 回退效果的缓出函数。
* @method backOut
* @param {Number} t The current time as a percentage of the total time.
* @return {Number} The correct value.
*/
backOut: function (k) {
var s = 1.70158;
return --k * k * ( ( s + 1 ) * k + s ) + 1;
},
/**
* !#en Easing in and out action with "back up" behavior.
* !#zh 回退效果的缓入缓出函数。
* @method backInOut
* @param {Number} t The current time as a percentage of the total time.
* @return {Number} The correct value.
*/
backInOut: function (k) {
var s = 1.70158 * 1.525;
if (( k *= 2 ) < 1) {
return 0.5 * ( k * k * ( ( s + 1 ) * k - s ) );
}
return 0.5 * ( ( k -= 2 ) * k * ( ( s + 1 ) * k + s ) + 2 );
},
// bounce
// easing equation function for a bounce (exponentially decaying parabolic bounce)
// @param t: Current time (in frames or seconds).
// @return: The correct value.
/**
* !#en Easing in action with bouncing effect.
* !#zh 弹跳效果的缓入函数。
* @method bounceIn
* @param {Number} t The current time as a percentage of the total time.
* @return {Number} The correct value.
*/
bounceIn: function (k) {
return 1 - easing.bounceOut(1 - k);
},
/**
* !#en Easing out action with bouncing effect.
* !#zh 弹跳效果的缓出函数。
* @method bounceOut
* @param {Number} t The current time as a percentage of the total time.
* @return {Number} The correct value.
*/
bounceOut: function (k) {
if (k < ( 1 / 2.75 )) {
return 7.5625 * k * k;
}
else if (k < ( 2 / 2.75 )) {
return 7.5625 * ( k -= ( 1.5 / 2.75 ) ) * k + 0.75;
}
else if (k < ( 2.5 / 2.75 )) {
return 7.5625 * ( k -= ( 2.25 / 2.75 ) ) * k + 0.9375;
}
else {
return 7.5625 * ( k -= ( 2.625 / 2.75 ) ) * k + 0.984375;
}
},
/**
* !#en Easing in and out action with bouncing effect.
* !#zh 弹跳效果的缓入缓出函数。
* @method bounceInOut
* @param {Number} t The current time as a percentage of the total time.
* @return {Number} The correct value.
*/
bounceInOut: function (k) {
if (k < 0.5) {
return easing.bounceIn(k * 2) * 0.5;
}
return easing.bounceOut(k * 2 - 1) * 0.5 + 0.5;
},
/**
* !#en Target will run action with smooth effect.
* !#zh 平滑效果函数。
* @method smooth
* @param {Number} t The current time as a percentage of the total time.
* @return {Number} The correct value.
*/
// t<=0: 0 | 0<t<1: 3*t^2 - 2*t^3 | t>=1: 1
smooth: function (t) {
if (t <= 0) {
return 0;
}
if (t >= 1) {
return 1;
}
return t * t * (3 - 2 * t);
},
/**
* !#en Target will run action with fade effect.
* !#zh 渐褪效果函数。
* @method fade
* @param {Number} t The current time as a percentage of the total time.
* @return {Number} The correct value.
*/
// t<=0: 0 | 0<t<1: 6*t^5 - 15*t^4 + 10*t^3 | t>=1: 1
fade: function (t) {
if (t <= 0) {
return 0;
}
if (t >= 1) {
return 1;
}
return t * t * t * (t * (t * 6 - 15) + 10);
},
};
function _makeOutIn (fnIn, fnOut) {
return function (k) {
if (k < 0.5) {
return fnOut(k * 2) / 2;
}
return fnIn(2 * k - 1) / 2 + 0.5;
};
}
easing.quadOutIn = _makeOutIn(easing.quadIn, easing.quadOut);
easing.cubicOutIn = _makeOutIn(easing.cubicIn, easing.cubicOut);
easing.quartOutIn = _makeOutIn(easing.quartIn, easing.quartOut);
easing.quintOutIn = _makeOutIn(easing.quintIn, easing.quintOut);
easing.sineOutIn = _makeOutIn(easing.sineIn, easing.sineOut);
easing.expoOutIn = _makeOutIn(easing.expoIn, easing.expoOut);
easing.circOutIn = _makeOutIn(easing.circIn, easing.circOut);
easing.backOutIn = _makeOutIn(easing.backIn, easing.backOut);
easing.bounceIn = function (k) { return 1 - easing.bounceOut(1 - k); };
easing.bounceInOut = function (k) {
if (k < 0.5) {
return easing.bounceIn(k * 2) * 0.5;
}
return easing.bounceOut(k * 2 - 1) * 0.5 + 0.5;
};
easing.bounceOutIn = _makeOutIn(easing.bounceIn, easing.bounceOut);
/**
* @module cc
*/
/**
* !#en This is a Easing instance.
* !#zh 这是一个 Easing 类实例。
* @property easing
* @type Easing
*/
cc.easing = module.exports = easing;

View File

@@ -0,0 +1,34 @@
/****************************************************************************
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
require('./bezier');
require('./easing');
require('./types');
require('./motion-path-helper');
require('./animation-curves');
require('./animation-clip');
require('./animation-manager');
require('./animation-state');
require('./animation-animator');

View File

@@ -0,0 +1,415 @@
/****************************************************************************
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
var DynamicAnimCurve = require('./animation-curves').DynamicAnimCurve;
var computeRatioByType = require('./animation-curves').computeRatioByType;
var bezier = require('./bezier').bezier;
var binarySearch = require('../core/utils/binary-search').binarySearchEpsilon;
var v2 = cc.v2;
function Curve (points) {
this.points = points || [];
this.beziers = [];
this.ratios = [];
this.progresses = [];
this.length = 0;
this.computeBeziers();
}
Curve.prototype.computeBeziers = function () {
this.beziers.length = 0;
this.ratios.length = 0;
this.progresses.length = 0;
this.length = 0;
var bezier;
for (var i = 1; i < this.points.length; i++) {
var startPoint = this.points[i - 1];
var endPoint = this.points[i];
bezier = new Bezier();
bezier.start = startPoint.pos;
bezier.startCtrlPoint = startPoint.out;
bezier.end = endPoint.pos;
bezier.endCtrlPoint = endPoint.in;
this.beziers.push(bezier);
this.length += bezier.getLength();
}
var current = 0;
for (var i = 0; i < this.beziers.length; i++) {
bezier = this.beziers[i];
this.ratios[i] = bezier.getLength() / this.length;
this.progresses[i] = current = current + this.ratios[i];
}
return this.beziers;
};
function Bezier () {
this.start = v2();
this.end = v2();
this.startCtrlPoint = v2(); // cp0, cp1
this.endCtrlPoint = v2(); // cp2, cp3
}
// Get point at relative position in curve according to arc length
// - u [0 .. 1]
Bezier.prototype.getPointAt = function ( u ) {
var t = this.getUtoTmapping( u );
return this.getPoint( t );
};
// Get point at time t
// - t [0 .. 1]
Bezier.prototype.getPoint = function ( t ) {
var x = bezier(this.start.x, this.startCtrlPoint.x, this.endCtrlPoint.x, this.end.x, t);
var y = bezier(this.start.y, this.startCtrlPoint.y, this.endCtrlPoint.y, this.end.y, t);
return new v2(x, y);
};
// Get total curve arc length
Bezier.prototype.getLength = function () {
var lengths = this.getLengths();
return lengths[ lengths.length - 1 ];
};
// Get list of cumulative segment lengths
Bezier.prototype.getLengths = function ( divisions ) {
if ( ! divisions ) divisions = (this.__arcLengthDivisions) ? (this.__arcLengthDivisions): 200;
if ( this.cacheArcLengths
&& ( this.cacheArcLengths.length === divisions + 1 )) {
//console.log( "cached", this.cacheArcLengths );
return this.cacheArcLengths;
}
var cache = [];
var current, last = this.getPoint( 0 ), vector = v2();
var p, sum = 0;
cache.push( 0 );
for ( p = 1; p <= divisions; p ++ ) {
current = this.getPoint ( p / divisions );
vector.x = last.x - current.x;
vector.y = last.y - current.y;
sum += vector.mag();
cache.push( sum );
last = current;
}
this.cacheArcLengths = cache;
return cache; // { sums: cache, sum:sum }; Sum is in the last element.
};
Bezier.prototype.getUtoTmapping = function ( u, distance ) {
var arcLengths = this.getLengths();
var i = 0, il = arcLengths.length;
var targetArcLength; // The targeted u distance value to get
if ( distance ) {
targetArcLength = distance;
} else {
targetArcLength = u * arcLengths[ il - 1 ];
}
//var time = Date.now();
// binary search for the index with largest value smaller than target u distance
var low = 0, high = il - 1, comparison;
while ( low <= high ) {
i = Math.floor( low + ( high - low ) / 2 ); // less likely to overflow, though probably not issue here, JS doesn't really have integers, all numbers are floats
comparison = arcLengths[ i ] - targetArcLength;
if ( comparison < 0 ) {
low = i + 1;
continue;
} else if ( comparison > 0 ) {
high = i - 1;
continue;
} else {
high = i;
break;
// DONE
}
}
i = high;
//console.log('b' , i, low, high, Date.now()- time);
if ( arcLengths[ i ] === targetArcLength ) {
var t = i / ( il - 1 );
return t;
}
// we could get finer grain at lengths, or use simple interpolatation between two points
var lengthBefore = arcLengths[ i ];
var lengthAfter = arcLengths[ i + 1 ];
var segmentLength = lengthAfter - lengthBefore;
// determine where we are between the 'before' and 'after' points
var segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength;
// add that fractional amount to t
var t = ( i + segmentFraction ) / ( il -1 );
return t;
};
function checkMotionPath(motionPath) {
if (!Array.isArray(motionPath)) return false;
for (let i = 0, l = motionPath.length; i < l; i++) {
let controls = motionPath[i];
if (!Array.isArray(controls) || controls.length !== 6) return false;
}
return true;
}
function sampleMotionPaths (motionPaths, data, duration, fps, target) {
function createControlPoints(array) {
if (array instanceof cc.Vec2) {
return {
in: array,
pos: array,
out: array
};
}
else if (Array.isArray(array) && array.length === 6) {
return {
in: v2(array[2], array[3]),
pos: v2(array[0], array[1]),
out: v2(array[4], array[5])
};
}
return {
in: cc.Vec2.ZERO,
pos: cc.Vec2.ZERO,
out: cc.Vec2.ZERO
};
}
let values = data.values = data.values.map(function (value) {
if (Array.isArray(value)) {
value = value.length === 2 ? cc.v2(value[0], value[1]) : cc.v3(value[0], value[1], value[2]);
}
return value;
});
if (motionPaths.length === 0 || values.length === 0) {
return;
}
let motionPathValid = false;
for (let i = 0; i < motionPaths.length; i++) {
let motionPath = motionPaths[i];
if (motionPath && !checkMotionPath(motionPath)) {
cc.errorID(3904, target ? target.name : '', 'position', i);
motionPath = null;
}
if (motionPath && motionPath.length > 0) {
motionPathValid = true;
break;
}
}
if (!motionPathValid) {
return;
}
if (values.length === 1) {
return;
}
var types = data.types;
var ratios = data.ratios;
var newValues = data.values = [];
var newTypes = data.types = [];
var newRatios = data.ratios = [];
function addNewDatas (value, type, ratio) {
newValues.push(value);
newTypes.push(type);
newRatios.push(ratio);
}
// ensure every ratio section's length is the same
var startRatioOffset = 0;
var EPSILON = 1e-6;
var newType = DynamicAnimCurve.Linear;
// do not need to compute last path
for (var i = 0, l = motionPaths.length; i < l-1; i++) {
var motionPath = motionPaths[i];
var ratio = ratios[i];
var nextRatio = ratios[i + 1];
var betweenRatio = nextRatio - ratio;
var value = values[i];
var nextValue = values[i + 1];
var type = types[i];
var results = [];
var progress = startRatioOffset / betweenRatio;
var speed = 1 / (betweenRatio * duration * fps);
var finalProgress;
if (motionPath && motionPath.length > 0) {
var points = [];
points.push(createControlPoints(value));
for (var j = 0, l2 = motionPath.length; j < l2; j++) {
var controlPoints = createControlPoints(motionPath[j]);
points.push(controlPoints);
}
points.push(createControlPoints(nextValue));
// create Curve to compute beziers
var curve = new Curve(points);
curve.computeBeziers();
// sample beziers
var progresses = curve.progresses;
while ( 1 - progress > EPSILON) {
finalProgress = progress;
finalProgress = computeRatioByType(finalProgress, type);
var pos, bezier, normal, length;
if (finalProgress < 0) {
bezier = curve.beziers[0];
length = (0 - finalProgress) * bezier.getLength();
normal = bezier.start.sub(bezier.endCtrlPoint).normalize();
pos = bezier.start.add(normal.mul(length));
}
else if (finalProgress > 1) {
bezier = curve.beziers[curve.beziers.length - 1];
length = (finalProgress - 1) * bezier.getLength();
normal = bezier.end.sub(bezier.startCtrlPoint).normalize();
pos = bezier.end.add(normal.mul(length));
}
else {
var bezierIndex = binarySearch(progresses, finalProgress);
if (bezierIndex < 0) bezierIndex = ~bezierIndex;
finalProgress -= bezierIndex > 0 ? progresses[bezierIndex - 1] : 0;
finalProgress = finalProgress / curve.ratios[bezierIndex];
pos = curve.beziers[bezierIndex].getPointAt(finalProgress);
}
results.push(pos);
progress += speed;
}
}
else {
while ( 1 - progress > EPSILON) {
finalProgress = progress;
finalProgress = computeRatioByType(finalProgress, type);
results.push(value.lerp(nextValue, finalProgress));
progress += speed;
}
}
newType = type === 'constant' ? type : DynamicAnimCurve.Linear;
for (var j = 0, l2 = results.length; j < l2; j++) {
var newRatio = ratio + startRatioOffset + speed * j * betweenRatio;
addNewDatas(results[j], newType, newRatio);
}
if (Math.abs(progress - 1) > EPSILON) // progress > 1
startRatioOffset = (progress - 1) * betweenRatio;
else
startRatioOffset = 0;
}
if (ratios[ratios.length - 1] !== newRatios[newRatios.length -1]) {
addNewDatas(values[values.length - 1], newType, ratios[ratios.length - 1]);
}
}
if (CC_TEST) {
cc._Test.sampleMotionPaths = sampleMotionPaths;
}
module.exports = {
sampleMotionPaths: sampleMotionPaths,
Curve: Curve,
Bezier: Bezier
};

View File

@@ -0,0 +1,170 @@
/****************************************************************************
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
var js = cc.js;
const debug = require('../core/CCDebug');
/**
* @class Playable
*
*/
function Playable () {
this._isPlaying = false;
this._isPaused = false;
this._stepOnce = false;
}
var prototype = Playable.prototype;
/**
* !#en Is playing or paused in play mode?
* !#zh 当前是否正在播放。
* @property isPlaying
* @type {boolean}
* @default false
* @readOnly
*/
js.get(prototype, 'isPlaying', function () {
return this._isPlaying;
}, true);
/**
* !#en Is currently paused? This can be true even if in edit mode(isPlaying == false).
* !#zh 当前是否正在暂停
* @property isPaused
* @type {boolean}
* @default false
* @readOnly
*/
js.get(prototype, 'isPaused', function () {
return this._isPaused;
}, true);
// virtual
var virtual = function () {};
/**
* @method onPlay
* @private
*/
prototype.onPlay = virtual;
/**
* @method onPause
* @private
*/
prototype.onPause = virtual;
/**
* @method onResume
* @private
*/
prototype.onResume = virtual;
/**
* @method onStop
* @private
*/
prototype.onStop = virtual;
/**
* @method onError
* @param {string} errorCode
* @private
*/
prototype.onError = virtual;
// public
/**
* !#en Play this animation.
* !#zh 播放动画。
* @method play
*/
prototype.play = function () {
if (this._isPlaying) {
if (this._isPaused) {
this._isPaused = false;
this.onResume();
}
else {
this.onError(debug.getError(3912));
}
}
else {
this._isPlaying = true;
this.onPlay();
}
};
/**
* !#en Stop this animation.
* !#zh 停止动画播放。
* @method stop
*/
prototype.stop = function () {
if (this._isPlaying) {
this._isPlaying = false;
this.onStop();
// need reset pause flag after onStop
this._isPaused = false;
}
};
/**
* !#en Pause this animation.
* !#zh 暂停动画。
* @method pause
*/
prototype.pause = function () {
if (this._isPlaying && !this._isPaused) {
this._isPaused = true;
this.onPause();
}
};
/**
* !#en Resume this animation.
* !#zh 重新播放动画。
* @method resume
*/
prototype.resume = function () {
if (this._isPlaying && this._isPaused) {
this._isPaused = false;
this.onResume();
}
};
/**
* !#en Perform a single frame step.
* !#zh 执行一帧动画。
* @method step
*/
prototype.step = function () {
this.pause();
this._stepOnce = true;
if (!this._isPlaying) {
this.play();
}
};
module.exports = Playable;

View File

@@ -0,0 +1,125 @@
/****************************************************************************
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
var WrapModeMask = {
Loop: 1 << 1,
ShouldWrap: 1 << 2,
// Reserved: 1 << 3,
PingPong: 1 << 4 | 1 << 1 | 1 << 2, // Loop, ShouldWrap
Reverse: 1 << 5 | 1 << 2, // ShouldWrap
};
/**
* !#en Specifies how time is treated when it is outside of the keyframe range of an Animation.
* !#zh 动画使用的循环模式。
* @enum WrapMode
* @memberof cc
*/
var WrapMode = cc.Enum({
/**
* !#en Reads the default wrap mode set higher up.
* !#zh 向 Animation Component 或者 AnimationClip 查找 wrapMode
* @property {Number} Default
*/
Default: 0,
/**
* !#en All iterations are played as specified.
* !#zh 动画只播放一遍
* @property {Number} Normal
*/
Normal: 1,
/**
* !#en All iterations are played in the reverse direction from the way they are specified.
* !#zh 从最后一帧或结束位置开始反向播放,到第一帧或开始位置停止
* @property {Number} Reverse
*/
Reverse: WrapModeMask.Reverse,
/**
* !#en When time reaches the end of the animation, time will continue at the beginning.
* !#zh 循环播放
* @property {Number} Loop
*/
Loop: WrapModeMask.Loop,
/**
* !#en All iterations are played in the reverse direction from the way they are specified.
* And when time reaches the start of the animation, time will continue at the ending.
* !#zh 反向循环播放
* @property {Number} LoopReverse
*/
LoopReverse: WrapModeMask.Loop | WrapModeMask.Reverse,
/**
* !#en Even iterations are played as specified, odd iterations are played in the reverse direction from the way they
* are specified.
* !#zh 从第一帧播放到最后一帧,然后反向播放回第一帧,到第一帧后再正向播放,如此循环
* @property {Number} PingPong
*/
PingPong: WrapModeMask.PingPong,
/**
* !#en Even iterations are played in the reverse direction from the way they are specified, odd iterations are played
* as specified.
* !#zh 从最后一帧开始反向播放,其他同 PingPong
* @property {Number} PingPongReverse
*/
PingPongReverse: WrapModeMask.PingPong | WrapModeMask.Reverse
});
cc.WrapMode = WrapMode;
// For internal
function WrappedInfo (info) {
if (info) {
this.set(info);
return;
}
this.ratio = 0;
this.time = 0;
this.direction = 1;
this.stopped = true;
this.iterations = 0;
this.frameIndex = undefined;
}
WrappedInfo.prototype.set = function (info) {
this.ratio = info.ratio;
this.time = info.time;
this.direction = info.direction;
this.stopped = info.stopped;
this.iterations = info.iterations;
this.frameIndex = info.frameIndex;
};
module.exports = {
WrapModeMask,
WrapMode,
WrappedInfo
};