陈鹏飞 4 years ago
parent
commit
ed16d30102
  1. 836
      src/app/working-area/charm.js
  2. 30
      src/app/working-area/working-area.component.ts

836
src/app/working-area/charm.js

@ -0,0 +1,836 @@
export class Charm {
constructor(renderingEngine = PIXI) {
if (renderingEngine === undefined) throw new Error("Please assign a rendering engine in the constructor before using charm.js");
//Find out which rendering engine is being used (the default is Pixi)
this.renderer = "";
//If the `renderingEngine` is Pixi, set up Pixi object aliases
if (renderingEngine.ParticleContainer && renderingEngine.Sprite) {
this.renderer = "pixi";
}
//An array to store the global tweens
this.globalTweens = [];
//An object that stores all the easing formulas
this.easingFormulas = {
//Linear
linear(x) {
return x;
},
//Smoothstep
smoothstep(x) {
return x * x * (3 - 2 * x);
},
smoothstepSquared(x) {
return Math.pow((x * x * (3 - 2 * x)), 2);
},
smoothstepCubed(x) {
return Math.pow((x * x * (3 - 2 * x)), 3);
},
//Acceleration
acceleration(x) {
return x * x;
},
accelerationCubed(x) {
return Math.pow(x * x, 3);
},
//Deceleration
deceleration(x) {
return 1 - Math.pow(1 - x, 2);
},
decelerationCubed(x) {
return 1 - Math.pow(1 - x, 3);
},
//Sine
sine(x) {
return Math.sin(x * Math.PI / 2);
},
sineSquared(x) {
return Math.pow(Math.sin(x * Math.PI / 2), 2);
},
sineCubed(x) {
return Math.pow(Math.sin(x * Math.PI / 2), 2);
},
inverseSine(x) {
return 1 - Math.sin((1 - x) * Math.PI / 2);
},
inverseSineSquared(x) {
return 1 - Math.pow(Math.sin((1 - x) * Math.PI / 2), 2);
},
inverseSineCubed(x) {
return 1 - Math.pow(Math.sin((1 - x) * Math.PI / 2), 3);
},
//Spline
spline(t, p0, p1, p2, p3) {
return 0.5 * (
(2 * p1) +
(-p0 + p2) * t +
(2 * p0 - 5 * p1 + 4 * p2 - p3) * t * t +
(-p0 + 3 * p1 - 3 * p2 + p3) * t * t * t
);
},
//Bezier curve
cubicBezier(t, a, b, c, d) {
let t2 = t * t;
let t3 = t2 * t;
return a + (-a * 3 + t * (3 * a - a * t)) * t + (3 * b + t * (-6 * b + b * 3 * t)) * t + (c * 3 - c * 3 * t) * t2 + d * t3;
}
};
//Add `scaleX` and `scaleY` properties to Pixi sprites
this._addScaleProperties = (sprite) => {
if (this.renderer === "pixi") {
if (!("scaleX" in sprite) && ("scale" in sprite) && ("x" in sprite.scale)) {
Object.defineProperty(
sprite,
"scaleX", {
get() {
return sprite.scale.x
},
set(value) {
sprite.scale.x = value
}
}
);
}
if (!("scaleY" in sprite) && ("scale" in sprite) && ("y" in sprite.scale)) {
Object.defineProperty(
sprite,
"scaleY", {
get() {
return sprite.scale.y
},
set(value) {
sprite.scale.y = value
}
}
);
}
}
};
}
//The low level `tweenProperty` function is used as the foundation
//for the the higher level tween methods.
tweenProperty(
sprite, //Sprite object
property, //String property
startValue, //Tween start value
endValue, //Tween end value
totalFrames, //Duration in frames
type = "smoothstep", //The easing type
yoyo = false, //Yoyo?
delayBeforeRepeat = 0 //Delay in frames before repeating
) {
//Create the tween object
let o = {};
//If the tween is a bounce type (a spline), set the
//start and end magnitude values
let typeArray = type.split(" ");
if (typeArray[0] === "bounce") {
o.startMagnitude = parseInt(typeArray[1]);
o.endMagnitude = parseInt(typeArray[2]);
}
//Use `o.start` to make a new tween using the current
//end point values
o.start = (startValue, endValue) => {
//Clone the start and end values so that any possible references to sprite
//properties are converted to ordinary numbers
o.startValue = JSON.parse(JSON.stringify(startValue));
o.endValue = JSON.parse(JSON.stringify(endValue));
o.playing = true;
o.totalFrames = totalFrames;
o.frameCounter = 0;
//Add the tween to the global `tweens` array. The `tweens` array is
//updated on each frame
this.globalTweens.push(o);
};
//Call `o.start` to start the tween
o.start(startValue, endValue);
//The `update` method will be called on each frame by the game loop.
//This is what makes the tween move
o.update = () => {
let time, curvedTime;
if (o.playing) {
//If the elapsed frames are less than the total frames,
//use the tweening formulas to move the sprite
if (o.frameCounter < o.totalFrames) {
//Find the normalized value
let normalizedTime = o.frameCounter / o.totalFrames;
//Select the correct easing function from the
//`ease` object’s library of easing functions
//If it's not a spline, use one of the ordinary easing functions
if (typeArray[0] !== "bounce") {
curvedTime = this.easingFormulas[type](normalizedTime);
}
//If it's a spline, use the `spline` function and apply the
//2 additional `type` array values as the spline's start and
//end points
else {
curvedTime = this.easingFormulas.spline(normalizedTime, o.startMagnitude, 0, 1, o.endMagnitude);
}
//Interpolate the sprite's property based on the curve
sprite[property] = (o.endValue * curvedTime) + (o.startValue * (1 - curvedTime));
o.frameCounter += 1;
}
//When the tween has finished playing, run the end tasks
else {
sprite[property] = o.endValue;
o.end();
}
}
};
//The `end` method will be called when the tween is finished
o.end = () => {
//Set `playing` to `false`
o.playing = false;
//Call the tween's `onComplete` method, if it's been assigned
if (o.onComplete) o.onComplete();
//Remove the tween from the `tweens` array
this.globalTweens.splice(this.globalTweens.indexOf(o), 1);
//If the tween's `yoyo` property is `true`, create a new tween
//using the same values, but use the current tween's `startValue`
//as the next tween's `endValue`
if (yoyo) {
this.wait(delayBeforeRepeat).then(() => {
o.start(o.endValue, o.startValue);
});
}
};
//Pause and play methods
o.play = () => o.playing = true;
o.pause = () => o.playing = false;
//Return the tween object
return o;
}
//`makeTween` is a general low-level method for making complex tweens
//out of multiple `tweenProperty` functions. Its one argument,
//`tweensToAdd` is an array containing multiple `tweenProperty` calls
makeTween(tweensToAdd) {
//Create an object to manage the tweens
let o = {};
//Create a `tweens` array to store the new tweens
o.tweens = [];
//Make a new tween for each array
tweensToAdd.forEach(tweenPropertyArguments => {
//Use the tween property arguments to make a new tween
let newTween = this.tweenProperty(...tweenPropertyArguments);
//Push the new tween into this object's internal `tweens` array
o.tweens.push(newTween);
});
//Add a counter to keep track of the
//number of tweens that have completed their actions
let completionCounter = 0;
//`o.completed` will be called each time one of the tweens
//finishes
o.completed = () => {
//Add 1 to the `completionCounter`
completionCounter += 1;
//If all tweens have finished, call the user-defined `onComplete`
//method, if it's been assigned. Reset the `completionCounter`
if (completionCounter === o.tweens.length) {
if (o.onComplete) o.onComplete();
completionCounter = 0;
}
};
//Add `onComplete` methods to all tweens
o.tweens.forEach(tween => {
tween.onComplete = () => o.completed();
});
//Add pause and play methods to control all the tweens
o.pause = () => {
o.tweens.forEach(tween => {
tween.playing = false;
});
};
o.play = () => {
o.tweens.forEach(tween => {
tween.playing = true;
});
};
//Return the tween object
return o;
}
/* High level tween methods */
//1. Simple tweens
//`fadeOut`
fadeOut(sprite, frames = 60) {
return this.tweenProperty(
sprite, "alpha", sprite.alpha, 0, frames, "sine"
);
}
//`fadeIn`
fadeIn(sprite, frames = 60) {
return this.tweenProperty(
sprite, "alpha", sprite.alpha, 1, frames, "sine"
);
}
//`pulse`
//Fades the sprite in and out at a steady rate.
//Set the `minAlpha` to something greater than 0 if you
//don't want the sprite to fade away completely
pulse(sprite, frames = 60, minAlpha = 0) {
return this.tweenProperty(
sprite, "alpha", sprite.alpha, minAlpha, frames, "smoothstep", true
);
}
//2. Complex tweens
slide(
sprite, endX, endY,
frames = 60, type = "smoothstep", yoyo = false, delayBeforeRepeat = 0
) {
return this.makeTween([
//Create the x axis tween
[sprite, "x", sprite.x, endX, frames, type, yoyo, delayBeforeRepeat],
//Create the y axis tween
[sprite, "y", sprite.y, endY, frames, type, yoyo, delayBeforeRepeat]
]);
}
breathe(
sprite, endScaleX = 0.8, endScaleY = 0.8,
frames = 60, yoyo = true, delayBeforeRepeat = 0
) {
//Add `scaleX` and `scaleY` properties to Pixi sprites
this._addScaleProperties(sprite);
return this.makeTween([
//Create the scaleX tween
[
sprite, "scaleX", sprite.scaleX, endScaleX,
frames, "smoothstepSquared", yoyo, delayBeforeRepeat
],
//Create the scaleY tween
[
sprite, "scaleY", sprite.scaleY, endScaleY,
frames, "smoothstepSquared", yoyo, delayBeforeRepeat
]
]);
}
scale(sprite, endScaleX = 0.5, endScaleY = 0.5, frames = 60) {
//Add `scaleX` and `scaleY` properties to Pixi sprites
this._addScaleProperties(sprite);
return this.makeTween([
//Create the scaleX tween
[
sprite, "scaleX", sprite.scaleX, endScaleX,
frames, "smoothstep", false
],
//Create the scaleY tween
[
sprite, "scaleY", sprite.scaleY, endScaleY,
frames, "smoothstep", false
]
]);
}
strobe(
sprite, scaleFactor = 1.3, startMagnitude = 10, endMagnitude = 20,
frames = 10, yoyo = true, delayBeforeRepeat = 0
) {
let bounce = "bounce " + startMagnitude + " " + endMagnitude;
//Add `scaleX` and `scaleY` properties to Pixi sprites
this._addScaleProperties(sprite);
return this.makeTween([
//Create the scaleX tween
[
sprite, "scaleX", sprite.scaleX, scaleFactor, frames,
bounce, yoyo, delayBeforeRepeat
],
//Create the scaleY tween
[
sprite, "scaleY", sprite.scaleY, scaleFactor, frames,
bounce, yoyo, delayBeforeRepeat
]
]);
}
wobble(
sprite,
scaleFactorX = 1.2,
scaleFactorY = 1.2,
frames = 10,
xStartMagnitude = 10,
xEndMagnitude = 10,
yStartMagnitude = -10,
yEndMagnitude = -10,
friction = 0.98,
yoyo = true,
delayBeforeRepeat = 0
) {
let bounceX = "bounce " + xStartMagnitude + " " + xEndMagnitude;
let bounceY = "bounce " + yStartMagnitude + " " + yEndMagnitude;
//Add `scaleX` and `scaleY` properties to Pixi sprites
this._addScaleProperties(sprite);
let o = this.makeTween([
//Create the scaleX tween
[
sprite, "scaleX", sprite.scaleX, scaleFactorX, frames,
bounceX, yoyo, delayBeforeRepeat
],
//Create the scaleY tween
[
sprite, "scaleY", sprite.scaleY, scaleFactorY, frames,
bounceY, yoyo, delayBeforeRepeat
]
]);
//Add some friction to the `endValue` at the end of each tween
o.tweens.forEach(tween => {
tween.onComplete = () => {
//Add friction if the `endValue` is greater than 1
if (tween.endValue > 1) {
tween.endValue *= friction;
//Set the `endValue` to 1 when the effect is finished and
//remove the tween from the global `tweens` array
if (tween.endValue <= 1) {
tween.endValue = 1;
this.removeTween(tween);
}
}
};
});
return o;
}
//3. Motion path tweens
followCurve(
sprite,
pointsArray,
totalFrames,
type = "smoothstep",
yoyo = false,
delayBeforeRepeat = 0
) {
//Create the tween object
let o = {};
//If the tween is a bounce type (a spline), set the
//start and end magnitude values
let typeArray = type.split(" ");
if (typeArray[0] === "bounce") {
o.startMagnitude = parseInt(typeArray[1]);
o.endMagnitude = parseInt(typeArray[2]);
}
//Use `tween.start` to make a new tween using the current
//end point values
o.start = (pointsArray) => {
o.playing = true;
o.totalFrames = totalFrames;
o.frameCounter = 0;
//Clone the points array
o.pointsArray = JSON.parse(JSON.stringify(pointsArray));
//Add the tween to the `globalTweens` array. The `globalTweens` array is
//updated on each frame
this.globalTweens.push(o);
};
//Call `tween.start` to start the first tween
o.start(pointsArray);
//The `update` method will be called on each frame by the game loop.
//This is what makes the tween move
o.update = () => {
let normalizedTime, curvedTime,
p = o.pointsArray;
if (o.playing) {
//If the elapsed frames are less than the total frames,
//use the tweening formulas to move the sprite
if (o.frameCounter < o.totalFrames) {
//Find the normalized value
normalizedTime = o.frameCounter / o.totalFrames;
//Select the correct easing function
//If it's not a spline, use one of the ordinary tween
//functions
if (typeArray[0] !== "bounce") {
curvedTime = this.easingFormulas[type](normalizedTime);
}
//If it's a spline, use the `spline` function and apply the
//2 additional `type` array values as the spline's start and
//end points
else {
//curve = tweenFunction.spline(n, type[1], 0, 1, type[2]);
curvedTime = this.easingFormulas.spline(normalizedTime, o.startMagnitude, 0, 1, o.endMagnitude);
}
//Apply the Bezier curve to the sprite's position
sprite.x = this.easingFormulas.cubicBezier(curvedTime, p[0][0], p[1][0], p[2][0], p[3][0]);
sprite.y = this.easingFormulas.cubicBezier(curvedTime, p[0][1], p[1][1], p[2][1], p[3][1]);
//Add one to the `elapsedFrames`
o.frameCounter += 1;
}
//When the tween has finished playing, run the end tasks
else {
//sprite[property] = o.endValue;
o.end();
}
}
};
//The `end` method will be called when the tween is finished
o.end = () => {
//Set `playing` to `false`
o.playing = false;
//Call the tween's `onComplete` method, if it's been
//assigned
if (o.onComplete) o.onComplete();
//Remove the tween from the global `tweens` array
this.globalTweens.splice(this.globalTweens.indexOf(o), 1);
//If the tween's `yoyo` property is `true`, reverse the array and
//use it to create a new tween
if (yoyo) {
this.wait(delayBeforeRepeat).then(() => {
o.pointsArray = o.pointsArray.reverse();
o.start(o.pointsArray);
});
}
};
//Pause and play methods
o.pause = () => {
o.playing = false;
};
o.play = () => {
o.playing = true;
};
//Return the tween object
return o;
}
walkPath(
sprite, //The sprite
originalPathArray, //A 2D array of waypoints
totalFrames = 300, //The duration, in frames
type = "smoothstep", //The easing type
loop = false, //Should the animation loop?
yoyo = false, //Shoud the direction reverse?
delayBetweenSections = 0 //Delay, in milliseconds, between sections
) {
//Clone the path array so that any possible references to sprite
//properties are converted into ordinary numbers
let pathArray = JSON.parse(JSON.stringify(originalPathArray));
//Figure out the duration, in frames, of each path section by
//dividing the `totalFrames` by the length of the `pathArray`
let frames = totalFrames / pathArray.length;
//Set the current point to 0, which will be the first waypoint
let currentPoint = 0;
//The `makePath` function creates a single tween between two points and
//then schedules the next path to be made after it
let makePath = (currentPoint) => {
//Use the `makeTween` function to tween the sprite's
//x and y position
let tween = this.makeTween([
//Create the x axis tween between the first x value in the
//current point and the x value in the following point
[
sprite,
"x",
pathArray[currentPoint][0],
pathArray[currentPoint + 1][0],
frames,
type
],
//Create the y axis tween in the same way
[
sprite,
"y",
pathArray[currentPoint][1],
pathArray[currentPoint + 1][1],
frames,
type
]
]);
//When the tween is complete, advance the `currentPoint` by one.
//Add an optional delay between path segments, and then make the
//next connecting path
tween.onComplete = () => {
//Advance to the next point
currentPoint += 1;
//If the sprite hasn't reached the end of the
//path, tween the sprite to the next point
if (currentPoint < pathArray.length - 1) {
this.wait(delayBetweenSections).then(() => {
tween = makePath(currentPoint);
});
}
//If we've reached the end of the path, optionally
//loop and yoyo it
else {
//Reverse the path if `loop` is `true`
if (loop) {
//Reverse the array if `yoyo` is `true`
if (yoyo) pathArray.reverse();
//Optionally wait before restarting
this.wait(delayBetweenSections).then(() => {
//Reset the `currentPoint` to 0 so that we can
//restart at the first point
currentPoint = 0;
//Set the sprite to the first point
sprite.x = pathArray[0][0];
sprite.y = pathArray[0][1];
//Make the first new path
tween = makePath(currentPoint);
//... and so it continues!
});
}
}
};
//Return the path tween to the main function
return tween;
};
//Make the first path using the internal `makePath` function (below)
let tween = makePath(currentPoint);
//Pass the tween back to the main program
return tween;
}
walkCurve(
sprite, //The sprite
pathArray, //2D array of Bezier curves
totalFrames = 300, //The duration, in frames
type = "smoothstep", //The easing type
loop = false, //Should the animation loop?
yoyo = false, //Should the direction reverse?
delayBeforeContinue = 0 //Delay, in milliseconds, between sections
) {
//Divide the `totalFrames` into sections for each part of the path
let frames = totalFrames / pathArray.length;
//Set the current curve to 0, which will be the first one
let currentCurve = 0;
//The `makePath` function
let makePath = (currentCurve) => {
//Use the custom `followCurve` function to make
//a sprite follow a curve
let tween = this.followCurve(
sprite,
pathArray[currentCurve],
frames,
type
);
//When the tween is complete, advance the `currentCurve` by one.
//Add an optional delay between path segments, and then make the
//next path
tween.onComplete = () => {
currentCurve += 1;
if (currentCurve < pathArray.length) {
this.wait(delayBeforeContinue).then(() => {
tween = makePath(currentCurve);
});
}
//If we've reached the end of the path, optionally
//loop and reverse it
else {
if (loop) {
if (yoyo) {
//Reverse order of the curves in the `pathArray`
pathArray.reverse();
//Reverse the order of the points in each curve
pathArray.forEach(curveArray => curveArray.reverse());
}
//After an optional delay, reset the sprite to the
//beginning of the path and make the next new path
this.wait(delayBeforeContinue).then(() => {
currentCurve = 0;
sprite.x = pathArray[0][0];
sprite.y = pathArray[0][1];
tween = makePath(currentCurve);
});
}
}
};
//Return the path tween to the main function
return tween;
};
//Make the first path
let tween = makePath(currentCurve);
//Pass the tween back to the main program
return tween;
}
//4. Utilities
/*
The `wait` method lets you set up a timed sequence of events
wait(1000)
.then(() => console.log("One"))
.then(() => wait(1000))
.then(() => console.log("Two"))
.then(() => wait(1000))
.then(() => console.log("Three"))
*/
wait(duration = 0) {
return new Promise((resolve, reject) => {
setTimeout(resolve, duration);
});
}
//A utility to remove tweens from the game
removeTween(tweenObject) {
//Remove the tween if `tweenObject` doesn't have any nested
//tween objects
if (!tweenObject.tweens) {
tweenObject.pause();
//array.splice(-1,1) will always remove last elemnt of array, so this
//extra check prevents that (Thank you, MCumic10! https://github.com/kittykatattack/charm/issues/5)
if (this.globalTweens.indexOf(tweenObject) != -1) {
this.globalTweens.splice(this.globalTweens.indexOf(tweenObject), 1);
}
//Otherwise, remove the nested tween objects
} else {
tweenObject.pause();
tweenObject.tweens.forEach(element => {
this.globalTweens.splice(this.globalTweens.indexOf(element), 1);
});
}
}
update() {
//Update all the tween objects in the `globalTweens` array
if (this.globalTweens.length > 0) {
for (let i = this.globalTweens.length - 1; i >= 0; i--) {
let tween = this.globalTweens[i];
if (tween) tween.update();
}
}
}
}

30
src/app/working-area/working-area.component.ts

@ -8,6 +8,7 @@ import {CacheTokenService} from '../http-interceptors/cache-token.service'; //
import * as ObjectID from 'bson-objectid'; import * as ObjectID from 'bson-objectid';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { isThisSecond } from 'date-fns'; import { isThisSecond } from 'date-fns';
import { Charm } from './charm';
@Component({ @Component({
@ -117,6 +118,10 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV
*/ */
public allowEdit = false; public allowEdit = false;
public c;
public animation;
public animationIcon;
public animationTime;
// 根据ID 找到数据 // 根据ID 找到数据
// 是否登录 // 是否登录
@ -227,6 +232,26 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV
} }
this.emit('backgroundScale', this.backgroundImage.scale.x); this.emit('backgroundScale', this.backgroundImage.scale.x);
} }
/**
*
* @param icon
*/
public moveIconToScreenCenter(icon) {
if (icon.parent === this.backgroundImage && icon.assetData.Type === 1) {
console.log(this.backgroundImage.position);
this.backgroundImage.pivot.set(icon.x, icon.y);
this.backgroundImage.position.set(771, 404);
clearTimeout(this.animationTime);
this.animation?.pause();
this.animationIcon?.scale.set(1);
this.animation = this.c.breathe(icon, 10, 10, 30, true, 0);
this.animationIcon = icon;
this.animationTime = setTimeout(() => {
this.animation?.pause();
this.animationIcon?.scale.set(1);
}, 5000);
}
}
/** /**
* *
*/ */
@ -243,7 +268,7 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV
const url = this.router.url; const url = this.router.url;
// tslint:disable-next-line: no-unused-expression // tslint:disable-next-line: no-unused-expression
url === this.verificationURL || url === this.verificationURLTwo ? this.isLogin() : null; url === this.verificationURL || url === this.verificationURLTwo ? this.isLogin() : null;
this.c = new Charm(PIXI);
this.createBackgroundImage(); this.createBackgroundImage();
// this.createPreviewSinglePointIcon(); // this.createPreviewSinglePointIcon();
// this.createPreviewLineSegment(); // this.createPreviewLineSegment();
@ -251,6 +276,7 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV
// this.createEnterPaintEndButton(); // this.createEnterPaintEndButton();
// this.backgroundImage.addChild(this.paintingLine); // this.backgroundImage.addChild(this.paintingLine);
this.on('select', obj => { this.on('select', obj => {
this.moveIconToScreenCenter(obj);
if (this.allowEdit) { if (this.allowEdit) {
if (obj instanceof MultipointIcon) { if (obj instanceof MultipointIcon) {
obj.setPointVisiable(true); obj.setPointVisiable(true);
@ -297,6 +323,8 @@ export class WorkingAreaComponent extends EventEmitter implements OnInit, AfterV
}); });
this.app.ticker.add((delta) => { this.app.ticker.add((delta) => {
this.c.update();
this.mousePosition = this.app.renderer.plugins.interaction.mouse.global; this.mousePosition = this.app.renderer.plugins.interaction.mouse.global;
this.previewSinglePointIcon.position = this.backgroundImage.toLocal(this.mousePosition); this.previewSinglePointIcon.position = this.backgroundImage.toLocal(this.mousePosition);

Loading…
Cancel
Save