Commit 031ae811 authored by 范雪寒's avatar 范雪寒

feat: 基础功能

parent 1b53952f
import TWEEN from '@tweenjs/tween.js';
interface AirWindow extends Window {
air: any;
curCtx: any;
}
declare let window: AirWindow;
class Sprite {
x = 0;
y = 0;
color = '';
radius = 0;
alive = false;
margin = 0;
angle = 0;
ctx;
constructor(ctx = null) {
if (!ctx) {
this.ctx = window.curCtx;
} else {
this.ctx = ctx;
}
}
update($event) {
this.draw();
}
draw() {
}
}
export class MySprite extends Sprite {
_width = 0;
_height = 0;
_anchorX = 0;
_anchorY = 0;
_offX = 0;
_offY = 0;
scaleX = 1;
scaleY = 1;
_alpha = 1;
rotation = 0;
visible = true;
skewX = 0;
skewY = 0;
_shadowFlag = false;
_shadowColor;
_shadowOffsetX = 0;
_shadowOffsetY = 0;
_shadowBlur = 5;
_radius = 0;
children = [this];
childDepandVisible = true;
childDepandAlpha = false;
img;
_z = 0;
init(imgObj = null, anchorX: number = 0.5, anchorY: number = 0.5) {
if (imgObj) {
this.img = imgObj;
this.width = this.img.width;
this.height = this.img.height;
}
this.anchorX = anchorX;
this.anchorY = anchorY;
}
setShadow(offX, offY, blur, color = 'rgba(0, 0, 0, 0.3)') {
this._shadowFlag = true;
this._shadowColor = color;
this._shadowOffsetX = offX;
this._shadowOffsetY = offY;
this._shadowBlur = blur;
}
setRadius(r) {
this._radius = r;
}
update($event = null) {
if (!this.visible && this.childDepandVisible) {
return;
}
this.draw();
}
draw() {
this.ctx.save();
this.drawInit();
this.updateChildren();
this.ctx.restore();
}
drawInit() {
this.ctx.translate(this.x, this.y);
this.ctx.rotate(this.rotation * Math.PI / 180);
this.ctx.scale(this.scaleX, this.scaleY);
this.ctx.globalAlpha = this.alpha;
this.ctx.transform(1, this.skewX, this.skewY, 1, 0, 0);
if (this._radius) {
const r = this._radius;
const w = this.width;
const h = this.height;
this.ctx.lineTo(-w / 2, h / 2); // 创建水平线
this.ctx.arcTo(-w / 2, -h / 2, -w / 2 + r, -h / 2, r);
this.ctx.arcTo(w / 2, -h / 2, w / 2, -h / 2 + r, r);
this.ctx.arcTo(w / 2, h / 2, w / 2 - r, h / 2, r);
this.ctx.arcTo(-w / 2, h / 2, -w / 2, h / 2 - r, r);
this.ctx.clip();
}
}
drawSelf() {
if (this._shadowFlag) {
this.ctx.shadowOffsetX = this._shadowOffsetX;
this.ctx.shadowOffsetY = this._shadowOffsetY;
this.ctx.shadowBlur = this._shadowBlur;
this.ctx.shadowColor = this._shadowColor;
} else {
this.ctx.shadowOffsetX = 0;
this.ctx.shadowOffsetY = 0;
this.ctx.shadowBlur = null;
this.ctx.shadowColor = null;
}
if (this.img) {
this.ctx.drawImage(this.img, this._offX, this._offY);
}
}
updateChildren() {
if (this.children.length <= 0) { return; }
for (const child of this.children) {
if (child === this) {
if (this.visible) {
this.drawSelf();
}
} else {
child.update();
}
}
}
load(url, anchorX = 0.5, anchorY = 0.5) {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => resolve(img);
img.onerror = reject;
img.src = url;
}).then(img => {
this.init(img, anchorX, anchorY);
return img;
});
}
addChild(child, z = 1) {
if (this.children.indexOf(child) === -1) {
this.children.push(child);
child._z = z;
child.parent = this;
}
this.children.sort((a, b) => {
return a._z - b._z;
});
if (this.childDepandAlpha) {
child.alpha = this.alpha;
}
}
removeChild(child) {
const index = this.children.indexOf(child);
if (index !== -1) {
this.children.splice(index, 1);
}
}
removeChildren() {
for (let i = 0; i < this.children.length; i++) {
if (this.children[i]) {
if (this.children[i] !== this) {
this.children.splice(i, 1);
i --;
}
}
}
}
_changeChildAlpha(alpha) {
for (const child of this.children) {
if (child !== this) {
child.alpha = alpha;
}
}
}
set alpha(v) {
this._alpha = v;
if (this.childDepandAlpha) {
this._changeChildAlpha(v);
}
}
get alpha() {
return this._alpha;
}
set width(v) {
this._width = v;
this.refreshAnchorOff();
}
get width() {
return this._width;
}
set height(v) {
this._height = v;
this.refreshAnchorOff();
}
get height() {
return this._height;
}
set anchorX(value) {
this._anchorX = value;
this.refreshAnchorOff();
}
get anchorX() {
return this._anchorX;
}
set anchorY(value) {
this._anchorY = value;
this.refreshAnchorOff();
}
get anchorY() {
return this._anchorY;
}
refreshAnchorOff() {
this._offX = -this._width * this.anchorX;
this._offY = -this._height * this.anchorY;
}
setScaleXY(value) {
this.scaleX = this.scaleY = value;
}
getBoundingBox() {
const getParentData = (item) => {
let px = item.x;
let py = item.y;
let sx = item.scaleX;
let sy = item.scaleY;
const parent = item.parent;
if (parent) {
const obj = getParentData(parent);
const _x = obj.px;
const _y = obj.py;
const _sx = obj.sx;
const _sy = obj.sy;
px = _x + item.x * _sx;
py = _y + item.y * _sy;
sx *= _sx;
sy *= _sy;
}
return {px, py, sx, sy};
};
const data = getParentData(this);
const x = data.px + this._offX * Math.abs(data.sx);
const y = data.py + this._offY * Math.abs(data.sy);
const width = this.width * Math.abs(data.sx);
const height = this.height * Math.abs(data.sy);
// const x = this.x + this._offX * Math.abs(this.scaleX);
// const y = this.y + this._offY * Math.abs(this.scaleY);
// const width = this.width * Math.abs(this.scaleX);
// const height = this.height * Math.abs(this.scaleY);
return {x, y, width, height};
}
}
export class ColorSpr extends MySprite {
r = 0;
g = 0;
b = 0;
createGSCanvas() {
if (!this.img) {
return;
}
const rect = this.getBoundingBox();
if (rect.width <= 1 || rect.height <= 1) {
return;
}
const c = this.ctx.getImageData(rect.x, rect.y, rect.width, rect.height);
for ( let i = 0; i < c.height; i++) {
for ( let j = 0; j < c.width; j++) {
const x = (i * 4) * c.width + ( j * 4 );
const r = c.data[x];
const g = c.data[x + 1];
const b = c.data[x + 2];
c.data[x] = this.r;
c.data[x + 1] = this.g;
c.data[x + 2] = this.b;
// c.data[x] = c.data[x + 1] = c.data[x + 2] = (r + g + b) / 3 ;
// // c.data[x + 3] = 255;
}
}
this.ctx.putImageData(c, rect.x, rect.y, 0, 0, rect.width, rect.height);
}
drawSelf() {
super.drawSelf();
this.createGSCanvas();
}
}
export class GrayscaleSpr extends MySprite {
grayScale = 120;
createGSCanvas() {
if (!this.img) {
return;
}
const rect = this.getBoundingBox();
const c = this.ctx.getImageData(rect.x, rect.y, rect.width, rect.height);
for ( let i = 0; i < c.height; i++) {
for ( let j = 0; j < c.width; j++) {
const x = (i * 4) * c.width + ( j * 4 );
const r = c.data[x];
const g = c.data[x + 1];
const b = c.data[x + 2];
// const a = c.data[x + 3];
c.data[x] = c.data[x + 1] = c.data[x + 2] = this.grayScale; // (r + g + b) / 3;
// c.data[x + 3] = 255;
}
}
this.ctx.putImageData(c, rect.x, rect.y, 0, 0, rect.width, rect.height);
}
drawSelf() {
super.drawSelf();
this.createGSCanvas();
}
}
export class BitMapLabel extends MySprite {
labelArr;
baseUrl;
setText(data, text) {
this.labelArr = [];
const labelArr = [];
const tmpArr = text.split('');
let totalW = 0;
let h = 0;
for (const tmp of tmpArr) {
const label = new MySprite(this.ctx);
label.init(data[tmp], 0);
this.addChild(label);
labelArr.push(label);
totalW += label.width;
h = label.height;
}
this.width = totalW;
this.height = h;
let offX = -totalW / 2;
for (const label of labelArr) {
label.x = offX;
offX += label.width;
}
this.labelArr = labelArr;
}
}
export class Label extends MySprite {
text: string;
// fontSize:String = '40px';
fontName = 'Verdana';
textAlign = 'left';
fontSize = 40;
fontColor = '#000000';
fontWeight = 900;
_maxWidth;
outline = 0;
outlineColor = '#ffffff';
// _shadowFlag = false;
// _shadowColor;
// _shadowOffsetX;
// _shadowOffsetY;
// _shadowBlur;
_outlineFlag = false;
_outLineWidth;
_outLineColor;
constructor(ctx = null) {
super(ctx);
this.init();
}
refreshSize() {
this.ctx.save();
this.ctx.font = `${this.fontSize}px ${this.fontName}`;
this.ctx.textAlign = this.textAlign;
this.ctx.textBaseline = 'middle';
this.ctx.fontWeight = this.fontWeight;
this._width = this.ctx.measureText(this.text).width;
this._height = this.fontSize;
this.refreshAnchorOff();
this.ctx.restore();
}
setMaxSize(w) {
this._maxWidth = w;
this.refreshSize();
if (this.width >= w) {
this.scaleX *= w / this.width;
this.scaleY *= w / this.width;
}
}
show(callBack = null) {
this.visible = true;
if (this.alpha >= 1) {
return;
}
const tween = new TWEEN.Tween(this)
.to({ alpha: 1 }, 800)
// .easing(TWEEN.Easing.Quadratic.Out) // Use an easing function to make the animation smooth.
.onComplete(() => {
if (callBack) {
callBack();
}
})
.start(); // Start the tween immediately.
}
// setShadow(offX = 0, offY = 2, blur = 2, color = 'rgba(0, 0, 0, 0.2)') {
//
// this._shadowFlag = true;
// this._shadowColor = color;
// // 将阴影向右移动15px,向上移动10px
// this._shadowOffsetX = 5;
// this._shadowOffsetY = 5;
// // 轻微模糊阴影
// this._shadowBlur = 5;
// }
setOutline(width = 5, color = '#ffffff') {
this._outlineFlag = true;
this._outLineWidth = width;
this._outLineColor = color;
}
drawText() {
// console.log('in drawText', this.text);
if (!this.text) { return; }
// if (this._shadowFlag) {
//
// this.ctx.shadowColor = this._shadowColor;
// // 将阴影向右移动15px,向上移动10px
// this.ctx.shadowOffsetX = this._shadowOffsetX;
// this.ctx.shadowOffsetY = this._shadowOffsetY;
// // 轻微模糊阴影
// this.ctx.shadowBlur = this._shadowBlur;
// }
this.ctx.font = `${this.fontSize}px ${this.fontName}`;
this.ctx.textAlign = this.textAlign;
this.ctx.textBaseline = 'middle';
this.ctx.fontWeight = this.fontWeight;
if (this._outlineFlag) {
this.ctx.lineWidth = this._outLineWidth;
this.ctx.strokeStyle = this._outLineColor;
this.ctx.strokeText(this.text, 0, 0);
}
this.ctx.fillStyle = this.fontColor;
if (this.outline > 0) {
this.ctx.lineWidth = this.outline;
this.ctx.strokeStyle = this.outlineColor;
this.ctx.strokeText(this.text, 0, 0);
}
this.ctx.fillText(this.text, 0, 0);
}
drawSelf() {
super.drawSelf();
this.drawText();
}
}
export class RichTextOld extends Label {
textArr = [];
fontSize = 40;
setText(text: string, words) {
let newText = text;
for (const word of words) {
const re = new RegExp(word, 'g');
newText = newText.replace( re, `#${word}#`);
// newText = newText.replace(word, `#${word}#`);
}
this.textArr = newText.split('#');
this.text = newText;
// this.setSize();
}
refreshSize() {
this.ctx.save();
this.ctx.font = `${this.fontSize}px ${this.fontName}`;
this.ctx.textAlign = this.textAlign;
this.ctx.textBaseline = 'middle';
this.ctx.fontWeight = this.fontWeight;
let curX = 0;
for (const text of this.textArr) {
const w = this.ctx.measureText(text).width;
curX += w;
}
this.width = curX;
this.height = this.fontSize;
this.refreshAnchorOff();
this.ctx.restore();
}
show(callBack = null) {
// console.log(' in show ');
this.visible = true;
// this.alpha = 0;
const tween = new TWEEN.Tween(this)
.to({ alpha: 1 }, 800)
// .easing(TWEEN.Easing.Quadratic.Out) // Use an easing function to make the animation smooth.
.onComplete(() => {
if (callBack) {
callBack();
}
})
.start(); // Start the tween immediately.
}
drawText() {
// console.log('in drawText', this.text);
if (!this.text) { return; }
this.ctx.font = `${this.fontSize}px ${this.fontName}`;
this.ctx.textAlign = this.textAlign;
this.ctx.textBaseline = 'middle';
this.ctx.fontWeight = 900;
this.ctx.lineWidth = 5;
this.ctx.strokeStyle = '#ffffff';
// this.ctx.strokeText(this.text, 0, 0);
this.ctx.fillStyle = '#000000';
// this.ctx.fillText(this.text, 0, 0);
let curX = 0;
for (let i = 0; i < this.textArr.length; i++) {
const w = this.ctx.measureText(this.textArr[i]).width;
if ((i + 1) % 2 === 0) {
this.ctx.fillStyle = '#c8171e';
} else {
this.ctx.fillStyle = '#000000';
}
this.ctx.fillText(this.textArr[i], curX, 0);
curX += w;
}
}
}
export class RichText extends Label {
disH = 30;
constructor(ctx?: any) {
super(ctx);
// this.dataArr = dataArr;
}
drawText() {
if (!this.text) {
return;
}
this.ctx.font = `${this.fontSize}px ${this.fontName}`;
this.ctx.textAlign = this.textAlign;
this.ctx.textBaseline = 'middle';
this.ctx.fontWeight = this.fontWeight;
this.ctx.fillStyle = this.fontColor;
const selfW = this.width * this.scaleX;
const chr = this.text.split(' ');
let temp = '';
const row = [];
const w = selfW - 80;
const disH = (this.fontSize + this.disH) * this.scaleY;
for (const c of chr) {
if (this.ctx.measureText(temp).width < w && this.ctx.measureText(temp + (c)).width <= w) {
temp += ' ' + c;
} else {
row.push(temp);
temp = ' ' + c;
}
}
row.push(temp);
const x = 0;
const y = -row.length * disH / 2;
// for (let b = 0 ; b < row.length; b++) {
// this.ctx.strokeText(row[b], x, y + ( b + 1 ) * disH ); // 每行字体y坐标间隔20
// }
if (this._outlineFlag) {
this.ctx.lineWidth = this._outLineWidth;
this.ctx.strokeStyle = this._outLineColor;
for (let b = 0 ; b < row.length; b++) {
this.ctx.strokeText(row[b], x, y + ( b + 1 ) * disH ); // 每行字体y坐标间隔20
}
// this.ctx.strokeText(this.text, 0, 0);
}
// this.ctx.fillStyle = '#ff7600';
for (let b = 0 ; b < row.length; b++) {
this.ctx.fillText(row[b], x, y + ( b + 1 ) * disH ); // 每行字体y坐标间隔20
}
}
drawSelf() {
super.drawSelf();
this.drawText();
}
}
export class LineRect extends MySprite {
lineColor = '#ffffff';
lineWidth = 10;
setSize(w, h) {
this.width = w;
this.height = h;
}
drawLine() {
this.ctx.beginPath();
this.ctx.moveTo(this._offX, this._offY);
this.ctx.lineTo(this._offX + this.width, this._offY);
this.ctx.lineTo(this._offX + this.width, this._offY + this.height);
this.ctx.lineTo(this._offX, this._offY + this.height);
this.ctx.closePath();
this.ctx.lineWidth = this.lineWidth;
// this.ctx.fillStyle = "rgb(2,33,42)"; //指定填充颜色
// this.ctx.fill(); //对多边形进行填充
this.ctx.strokeStyle = this.lineColor; // "#ffffff";
this.ctx.stroke();
}
drawSelf() {
super.drawSelf();
this.drawLine();
}
}
export class ShapeRect extends MySprite {
fillColor = '#FF0000';
setSize(w, h) {
this.width = w;
this.height = h;
// console.log('w:', w);
// console.log('h:', h);
}
drawShape() {
this.ctx.fillStyle = this.fillColor;
this.ctx.fillRect(this._offX, this._offY, this.width, this.height);
}
drawSelf() {
super.drawSelf();
this.drawShape();
}
}
export class ShapeCircle extends MySprite {
fillColor = '#FF0000';
radius = 0;
setRadius(r) {
this.anchorX = this.anchorY = 0.5;
this.radius = r;
this.width = r * 2;
this.height = r * 2;
}
drawShape() {
this.ctx.beginPath();
this.ctx.fillStyle = this.fillColor;
this.ctx.arc(0, 0, this.radius, 0, angleToRadian(360));
this.ctx.fill();
}
drawSelf() {
super.drawSelf();
this.drawShape();
}
}
export class ShapeRectNew extends MySprite {
radius = 0;
fillColor = '#ffffff';
strokeColor = '#000000';
fill = true;
stroke = false;
lineWidth = 1;
setSize(w, h, r) {
this.width = w;
this.height = h;
this.radius = r;
}
setOutLine(color, lineWidth) {
this.stroke = true;
this.strokeColor = color;
this.lineWidth = lineWidth;
}
drawShape() {
const ctx = this.ctx;
const width = this.width;
const height = this.height;
const radius = this.radius;
ctx.save();
ctx.beginPath(0);
// 从右下角顺时针绘制,弧度从0到1/2PI
ctx.arc(width - radius, height - radius, radius, 0, Math.PI / 2);
// 矩形下边线
ctx.lineTo(radius, height);
// 左下角圆弧,弧度从1/2PI到PI
ctx.arc(radius, height - radius, radius, Math.PI / 2, Math.PI);
// 矩形左边线
ctx.lineTo(0, radius);
// 左上角圆弧,弧度从PI到3/2PI
ctx.arc(radius, radius, radius, Math.PI, Math.PI * 3 / 2);
// 上边线
ctx.lineTo(width - radius, 0);
// 右上角圆弧
ctx.arc(width - radius, radius, radius, Math.PI * 3 / 2, Math.PI * 2);
// 右边线
ctx.lineTo(width, height - radius);
ctx.closePath();
if (this.fill) {
ctx.fillStyle = this.fillColor;
ctx.fill();
}
if (this.stroke) {
ctx.lineWidth = this.lineWidth;
ctx.strokeStyle = this.strokeColor;
ctx.stroke();
}
ctx.restore();
}
drawSelf() {
super.drawSelf();
this.drawShape();
}
}
export class MyAnimation extends MySprite {
frameArr = [];
frameIndex = 0;
playFlag = false;
lastDateTime;
curDelay = 0;
loop = false;
playEndFunc;
delayPerUnit = 1;
restartFlag = false;
reverseFlag = false;
addFrameByImg(img) {
const spr = new MySprite(this.ctx);
spr.init(img);
this._refreshSize(img);
spr.visible = false;
this.addChild(spr);
this.frameArr.push(spr);
this.frameArr[this.frameIndex].visible = true;
}
addFrameByUrl(url) {
const spr = new MySprite(this.ctx);
spr.load(url).then(img => {
this._refreshSize(img);
});
spr.visible = false;
this.addChild(spr);
this.frameArr.push(spr);
this.frameArr[this.frameIndex].visible = true;
}
_refreshSize(img: any) {
if (this.width < img.width) {
this.width = img.width;
}
if (this.height < img.height) {
this.height = img.height;
}
}
play() {
this.playFlag = true;
this.lastDateTime = new Date().getTime();
}
stop() {
this.playFlag = false;
}
replay() {
this.restartFlag = true;
this.play();
}
reverse() {
this.reverseFlag = !this.reverseFlag;
this.frameArr.reverse();
this.frameIndex = 0;
}
showAllFrame() {
for (const frame of this.frameArr ) {
frame.alpha = 1;
}
}
hideAllFrame() {
for (const frame of this.frameArr) {
frame.alpha = 0;
}
}
playEnd() {
this.playFlag = false;
this.curDelay = 0;
this.frameArr[this.frameIndex].visible = true;
if (this.playEndFunc) {
this.playEndFunc();
this.playEndFunc = null;
}
}
updateFrame() {
if (this.frameArr[this.frameIndex]) {
this.frameArr[this.frameIndex].visible = false;
}
this.frameIndex ++;
if (this.frameIndex >= this.frameArr.length) {
if (this.loop) {
this.frameIndex = 0;
} else if (this.restartFlag) {
this.restartFlag = false;
this.frameIndex = 0;
} else {
this.frameIndex -- ;
this.playEnd();
return;
}
}
this.frameArr[this.frameIndex].visible = true;
}
_updateDelay(delay) {
this.curDelay += delay;
if (this.curDelay < this.delayPerUnit) {
return;
}
this.curDelay -= this.delayPerUnit;
this.updateFrame();
}
_updateLastDate() {
if (!this.playFlag) { return; }
let delay = 0;
if (this.lastDateTime) {
delay = (new Date().getTime() - this.lastDateTime) / 1000;
}
this.lastDateTime = new Date().getTime();
this._updateDelay(delay);
}
update($event: any = null) {
super.update($event);
this._updateLastDate();
}
}
// --------=========== util func =============-------------
export function tweenChange(item, obj, time = 0.8, callBack = null, easing = null, update = null) {
const tween = new TWEEN.Tween(item).to(obj, time * 1000);
if (callBack) {
tween.onComplete(() => {
callBack();
});
}
if (easing) {
tween.easing(easing);
}
if (update) {
tween.onUpdate( (a, b) => {
update(a, b);
});
}
tween.start();
return tween;
}
export function rotateItem(item, rotation, time = 0.8, callBack = null, easing = null) {
const tween = new TWEEN.Tween(item).to({ rotation }, time * 1000);
if (callBack) {
tween.onComplete(() => {
callBack();
});
}
if (easing) {
tween.easing(easing);
}
tween.start();
}
export function scaleItem(item, scale, time = 0.8, callBack = null, easing = null) {
const tween = new TWEEN.Tween(item).to({ scaleX: scale, scaleY: scale}, time * 1000);
if (callBack) {
tween.onComplete(() => {
callBack();
});
}
if (easing) {
tween.easing(easing);
}
tween.start();
return tween;
}
export function moveItem(item, x, y, time = 0.8, callBack = null, easing = null) {
const tween = new TWEEN.Tween(item).to({ x, y}, time * 1000);
if (callBack) {
tween.onComplete(() => {
callBack();
});
}
if (easing) {
tween.easing(easing);
}
tween.start();
return tween;
}
export function endShow(item, s = 1) {
item.scaleX = item.scaleY = 0;
item.alpha = 0;
const tween = new TWEEN.Tween(item)
.to({ alpha: 1, scaleX: s, scaleY: s }, 800)
.easing(TWEEN.Easing.Elastic.Out) // Use an easing function to make the animation smooth.
.onComplete(() => {
})
.start();
}
export function hideItem(item, time = 0.8, callBack = null, easing = null) {
if (item.alpha === 0) {
return;
}
const tween = new TWEEN.Tween(item)
.to({alpha: 0}, time * 1000)
// .easing(TWEEN.Easing.Quadratic.Out) // Use an easing function to make the animation smooth.
.onComplete(() => {
if (callBack) {
callBack();
}
});
if (easing) {
tween.easing(easing);
}
tween.start();
}
export function showItem(item, time = 0.8, callBack = null, easing = null) {
if (item.alpha === 1) {
if (callBack) {
callBack();
}
return;
}
item.visible = true;
const tween = new TWEEN.Tween(item)
.to({alpha: 1}, time * 1000)
// .easing(TWEEN.Easing.Quadratic.Out) // Use an easing function to make the animation smooth.
.onComplete(() => {
if (callBack) {
callBack();
}
});
if (easing) {
tween.easing(easing);
}
tween.start();
}
export function alphaItem(item, alpha, time = 0.8, callBack = null, easing = null) {
const tween = new TWEEN.Tween(item)
.to({alpha}, time * 1000)
// .easing(TWEEN.Easing.Quadratic.Out) // Use an easing function to make the animation smooth.
.onComplete(() => {
if (callBack) {
callBack();
}
});
if (easing) {
tween.easing(easing);
}
tween.start();
}
export function showStar(item, time = 0.8, callBack = null, easing = null) {
const tween = new TWEEN.Tween(item)
.to({alpha: 1, scale: 1}, time * 1000)
// .easing(TWEEN.Easing.Quadratic.Out) // Use an easing function to make the animation smooth.
.onComplete(() => {
if (callBack) {
callBack();
}
});
if (easing) {
tween.easing(easing);
}
tween.start();
}
export function randomSortByArr(arr) {
const newArr = [];
const tmpArr = arr.concat();
while (tmpArr.length > 0) {
const randomIndex = Math.floor( tmpArr.length * Math.random() );
newArr.push(tmpArr[randomIndex]);
tmpArr.splice(randomIndex, 1);
}
return newArr;
}
export function radianToAngle(radian) {
return radian * 180 / Math.PI;
// 角度 = 弧度 * 180 / Math.PI;
}
export function angleToRadian(angle) {
return angle * Math.PI / 180;
// 弧度= 角度 * Math.PI / 180;
}
export function getPosByAngle(angle, len) {
const radian = angle * Math.PI / 180;
const x = Math.sin(radian) * len;
const y = Math.cos(radian) * len;
return {x, y};
}
export function getAngleByPos(px, py, mx, my) {
const x = Math.abs(px - mx);
const y = Math.abs(py - my);
const z = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
const cos = y / z;
const radina = Math.acos(cos); // 用反三角函数求弧度
let angle = Math.floor(180 / (Math.PI / radina) * 100) / 100; // 将弧度转换成角度
if (mx > px && my > py) {// 鼠标在第四象限
angle = 180 - angle;
}
if (mx === px && my > py) {// 鼠标在y轴负方向上
angle = 180;
}
if (mx > px && my === py) {// 鼠标在x轴正方向上
angle = 90;
}
if (mx < px && my > py) {// 鼠标在第三象限
angle = 180 + angle;
}
if (mx < px && my === py) {// 鼠标在x轴负方向
angle = 270;
}
if (mx < px && my < py) {// 鼠标在第二象限
angle = 360 - angle;
}
// console.log('angle: ', angle);
return angle;
}
export function removeItemFromArr(arr, item) {
const index = arr.indexOf(item);
if (index !== -1) {
arr.splice(index, 1);
}
}
export function circleMove(item, x0, y0, time = 2, addR = 360, xPer = 1, yPer = 1, callBack = null, easing = null) {
const r = getPosDistance(item.x, item.y, x0, y0);
let a = getAngleByPos(item.x, item.y, x0, y0);
a += 90;
const obj = {r, a};
item._circleAngle = a;
const targetA = a + addR;
const tween = new TWEEN.Tween(item).to({_circleAngle: targetA}, time * 1000);
if (callBack) {
tween.onComplete(() => {
callBack();
});
}
if (easing) {
tween.easing(easing);
}
tween.onUpdate( (item, progress) => {
// console.log(item._circleAngle);
const r = obj.r;
const a = item._circleAngle;
const x = x0 + r * xPer * Math.cos(a * Math.PI / 180);
const y = y0 + r * yPer * Math.sin(a * Math.PI / 180);
item.x = x;
item.y = y;
// obj.a ++;
});
tween.start();
}
export function getPosDistance(sx, sy, ex, ey) {
const _x = ex - sx;
const _y = ey - sy;
const len = Math.sqrt( Math.pow(_x, 2) + Math.pow(_y, 2) );
return len;
}
export function delayCall(callback, second) {
const tween = new TWEEN.Tween(this)
.delay(second * 1000)
.onComplete(() => {
if (callback) {
callback();
}
})
.start();
}
export function formatTime(fmt, date) {
// "yyyy-MM-dd HH:mm:ss";
const o = {
'M+': date.getMonth() + 1, // 月份
'd+': date.getDate(), // 日
'h+': date.getHours(), // 小时
'm+': date.getMinutes(), // 分
's+': date.getSeconds(), // 秒
'q+': Math.floor((date.getMonth() + 3) / 3), // 季度
S: date.getMilliseconds() // 毫秒
};
if (/(y+)/.test(fmt)) { fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length)); }
for (const k in o) {
if (new RegExp('(' + k + ')').test(fmt)) { fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1)
? (o[k]) : (('00' + o[k]).substr(('' + o[k]).length)));
}
}
return fmt;
}
export function getMinScale(item, maxLen) {
const sx = maxLen / item.width;
const sy = maxLen / item.height;
const minS = Math.min(sx, sy);
return minS;
}
export function jelly(item, time = 0.7) {
if (item.jellyTween) {
TWEEN.remove(item.jellyTween);
}
const t = time / 9;
const baseSX = item.scaleX;
const baseSY = item.scaleY;
let index = 0;
const run = () => {
if (index >= arr.length) {
item.jellyTween = null;
return;
}
const data = arr[index];
const t = tweenChange(item, {scaleX: data[0], scaleY: data[1]}, data[2], () => {
index ++;
run();
}, TWEEN.Easing.Sinusoidal.InOut);
item.jellyTween = t;
};
const arr = [
[baseSX * 1.1, baseSY * 0.9, t],
[baseSX * 0.98, baseSY * 1.02, t * 2],
[baseSX * 1.02, baseSY * 0.98, t * 2],
[baseSX * 0.99, baseSY * 1.01, t * 2],
[baseSX * 1.0, baseSY * 1.0, t * 2],
];
run();
}
export function showPopParticle(img, pos, parent, num = 20, minLen = 40, maxLen = 80, showTime = 0.4) {
for (let i = 0; i < num; i ++) {
const particle = new MySprite();
particle.init(img);
particle.x = pos.x;
particle.y = pos.y;
parent.addChild(particle);
const randomR = 360 * Math.random();
particle.rotation = randomR;
const randomS = 0.3 + Math.random() * 0.7;
particle.setScaleXY(randomS * 0.3);
const randomX = Math.random() * 20 - 10;
particle.x += randomX;
const randomY = Math.random() * 20 - 10;
particle.y += randomY;
const randomL = minLen + Math.random() * (maxLen - minLen);
const randomA = 360 * Math.random();
const randomT = getPosByAngle(randomA, randomL);
moveItem(particle, particle.x + randomT.x, particle.y + randomT.y, showTime, () => {
}, TWEEN.Easing.Exponential.Out);
// scaleItem(particle, 0, 0.6, () => {
//
// });
scaleItem(particle, randomS, 0.6, () => {
}, TWEEN.Easing.Exponential.Out);
setTimeout(() => {
hideItem(particle, 0.4, () => {
}, TWEEN.Easing.Cubic.In);
}, showTime * 0.5 * 1000);
}
}
export function shake(item, time = 0.5, callback = null, rate = 1) {
if (item.shakeTween) {
return;
}
item.shakeTween = true;
const offX = 15 * item.scaleX * rate;
const offY = 15 * item.scaleX * rate;
const baseX = item.x;
const baseY = item.y;
const easing = TWEEN.Easing.Sinusoidal.InOut;
const move4 = () => {
moveItem(item, baseX, baseY, time / 4, () => {
item.shakeTween = false;
if (callback) {
callback();
}
}, easing);
};
const move3 = () => {
moveItem(item, baseX + offX / 4, baseY + offY / 4, time / 4, () => {
move4();
}, easing);
};
const move2 = () => {
moveItem(item, baseX - offX / 4 * 3, baseY - offY / 4 * 3, time / 4, () => {
move3();
}, easing);
};
const move1 = () => {
moveItem(item, baseX + offX, baseY + offY, time / 7.5, () => {
move2();
}, easing);
};
move1();
}
// --------------- custom class --------------------
<!--
<div class="preview-box" #wrap>
<canvas id="canvas" #canvas></canvas>
</div> -->
\ No newline at end of file
.preview-box {
margin: auto;
width: 95%;
height: 35vw;
border: 2px dashed #ddd;
border-radius: 0.5rem;
background-color: #fafafa;
text-align: center;
color: #aaa;
.preview-img {
height: 100%;
width: auto;
}
}
import {
Component,
ElementRef,
EventEmitter,
HostListener,
Input,
OnChanges,
OnDestroy,
OnInit,
Output,
ViewChild
} from '@angular/core';
import { Subject } from 'rxjs';
import { debounceTime } from 'rxjs/operators';
import TWEEN from '@tweenjs/tween.js';
import { getMinScale } from "../../play/Unit";
import { tar } from "compressing";
@Component({
selector: 'app-preview-edit-mode',
templateUrl: './preview-edit-mode.component.html',
styleUrls: ['./preview-edit-mode.component.scss']
})
export class PreviewEditModeComponent implements OnInit, OnDestroy, OnChanges {
ngOnInit() {
}
ngOnDestroy() {
}
ngOnChanges() {
}
}
...@@ -6,3 +6,15 @@ ...@@ -6,3 +6,15 @@
height: 100%; height: 100%;
} }
.border {
border-radius: 20px;
border-style: dashed;
padding: 20px;
margin: 20px;
/*width: 500px; */
/*//border-radius: 20px;*/
/*//border-width: 2px;*/
/*//border-color: #000000;*/
}
\ No newline at end of file
<div class="model-content"> <div class="model-content">
<div class="border" style="width: 650px; height: 190px;">
<span style="float: left; height: 30px; font-size: 18px;">标题字母:</span>
<input type="text" style="width: 50px;" nz-input [(ngModel)]="item.tittle.word" (blur)="save()">
<br>
<br>
<span style="float: left; height: 30px; font-size: 18px;">标题:</span>
<input type="text" style="width: 550px;" nz-input [(ngModel)]="item.tittle.text" (blur)="save()">
<br>
<br>
<span style="float: left; height: 30px; font-size: 18px;">标题音频:</span>
<app-audio-recorder
[audioUrl]="item.tittle.audio"
(audioUploaded)="onTittleAudioUploadSuccess($event, 'audio_url')"
></app-audio-recorder>
</div>
<div *ngFor="let it of item.carrots; let i = index" style="float: left; width: 350px;">
<div class="border" style="width: 300px; float: left;">
<span style="float: left; height: 30px; font-size: 18px;">胡萝卜:</span>
<br><br>
<app-upload-image-with-preview
[picUrl]="it.carrotImg"
(imageUploaded)="onImageUploadSuccess($event, 'carrot', i)"
></app-upload-image-with-preview>
<br><br>
<span style="float: left; height: 30px; font-size: 18px;">坑:</span>
<br><br>
<app-upload-image-with-preview
[picUrl]="it.holeImg"
(imageUploaded)="onImageUploadSuccess($event, 'hole', i)"
></app-upload-image-with-preview>
<div *ngIf="(item.carrots.length > 5)">
<div style="float: none; padding-top: 30px;">
<button style="width: 100%; height: 45px; float: none;" nz-button nzType="dashed" class="add-btn" (click)="deleteCarrot(i)">
删除
</button>
</div>
</div>
  <div style="clear:both; height:0; font-size:1px; line-height:0px;"></div>
</div>
</div>
<div *ngIf="(item.carrots.length < 8)">
<div style="width: 300px; float: left; height: 280px; padding: 20px;" nz-col nzSpan="8" class="add-btn-box" >
<button style=" margin: auto; width: 100%; height: 100%;" nz-button nzType="dashed" class="add-btn" (click)="addCarrot()">
<i nz-icon nzType="plus-circle" nzTheme="outline"></i>
Add
</button>
</div>
</div>
 <div style="clear:both; height:0; font-size:1px; line-height:0px;"></div>
<div style="float: left; width: 100%">
<app-custom-hot-zone
[bgItem]="{}"
[isHasRect]="false"
[isHasText]="false"
[item]="item"
[hotZoneItemArr]="item.targets"
[refreshCaller]="caller"
(save)="saveData($event)"
>
</app-custom-hot-zone>
<!-- <app-preview
></app-preview> -->
</div>
<app-custom-hot-zone
[bgItem]="item.bgItem"
[hotZoneItemArr]="item.hotZoneItemArr"
(save)="saveData($event)"
>
</app-custom-hot-zone>
<!-- <!--
<div style="position: absolute; left: 200px; top: 100px; width: 800px;"> <div style="position: absolute; left: 200px; top: 100px; width: 800px;">
......
...@@ -10,21 +10,57 @@ import { Component, EventEmitter, Input, OnDestroy, OnChanges, OnInit, Output, A ...@@ -10,21 +10,57 @@ import { Component, EventEmitter, Input, OnDestroy, OnChanges, OnInit, Output, A
export class FormComponent implements OnInit, OnChanges, OnDestroy { export class FormComponent implements OnInit, OnChanges, OnDestroy {
// 储存数据用 // 储存数据用
saveKey = "test_0011"; saveKey = 'ym-2-30';
// 储存对象 // 储存对象
item; item;
caller: any = {};
constructor(private appRef: ApplicationRef, private changeDetectorRef: ChangeDetectorRef) { constructor(private appRef: ApplicationRef, private changeDetectorRef: ChangeDetectorRef) {
} }
deleteCarrot(idx) {
this.item.carrots.splice(idx, 1);
this.refreshBackground();
this.save();
}
addCarrot() {
this.item.carrots.push({
carrotImg: '',
holeImg: '',
});
this.refreshBackground();
this.save();
}
ngOnInit() { ngOnInit() {
this.item = { this.item = {
bgItem: '', tittle: {
hotZoneItemArr: [] word: "",
text: "",
audio: ""
},
carrots: [{
carrotImg: '',
holeImg: '',
}, {
carrotImg: '',
holeImg: '',
}, {
carrotImg: '',
holeImg: '',
}, {
carrotImg: '',
holeImg: '',
}, {
carrotImg: '',
holeImg: '',
}],
targets: []
}; };
// 获取存储的数据 // 获取存储的数据
...@@ -56,14 +92,19 @@ export class FormComponent implements OnInit, OnChanges, OnDestroy { ...@@ -56,14 +92,19 @@ export class FormComponent implements OnInit, OnChanges, OnDestroy {
} }
refreshBackground() {
this.caller.refreshBackground();
}
/** /**
* 储存图片数据 * 储存图片数据
* @param e * @param e
*/ */
onImageUploadSuccess(e, key) { onImageUploadSuccess(e, key, idx) {
// this.item[key] = e.url;
this.item[key] = e.url; this.item.carrots[idx][key + 'Img'] = e.url;
this.refreshBackground();
this.save(); this.save();
} }
...@@ -78,6 +119,17 @@ export class FormComponent implements OnInit, OnChanges, OnDestroy { ...@@ -78,6 +119,17 @@ export class FormComponent implements OnInit, OnChanges, OnDestroy {
} }
saveData(e) { saveData(e) {
this.item.targets = [];
e.hotZoneItemArr.forEach((target) => {
console.log('target.rect = ' + JSON.stringify(target.rect));
this.item.targets.push(target);
});
this.save();
}
// 标题音频
onTittleAudioUploadSuccess(e, key) {
this.item.tittle.audio = e.url;
this.save(); this.save();
} }
......
...@@ -7,17 +7,29 @@ export let defaultData = { ...@@ -7,17 +7,29 @@ export let defaultData = {
}, },
carrots: [{ carrots: [{
carrotImg: '', carrotImg: 'carrot',
holeImg: '', holeImg: 'hole',
}, {
carrotImg: 'carrot',
holeImg: 'hole',
}, {
carrotImg: 'carrot',
holeImg: 'hole',
}, {
carrotImg: 'carrot',
holeImg: 'hole',
}, {
carrotImg: 'carrot',
holeImg: 'hole',
}, {
carrotImg: 'carrot',
holeImg: 'hole',
}, {
carrotImg: 'carrot',
holeImg: 'hole',
}, {
carrotImg: 'carrot',
holeImg: 'hole',
}], }],
targets: [{ targets: []
img: '',
x: 0,
y: 0,
width: 1,
height: 1,
scaleX: 1,
scaleY: 1,
audio: ''
}]
}; };
\ No newline at end of file
...@@ -111,6 +111,7 @@ export class Game { ...@@ -111,6 +111,7 @@ export class Game {
typeof (this._currentTouchSprite._onTouchEndListener) == 'function') { typeof (this._currentTouchSprite._onTouchEndListener) == 'function') {
this._currentTouchSprite._onTouchEndListener(pos); this._currentTouchSprite._onTouchEndListener(pos);
} }
this._currentTouchSprite = null;
} }
} }
......
...@@ -48,9 +48,14 @@ export class MyGame extends Game { ...@@ -48,9 +48,14 @@ export class MyGame extends Game {
let imgUrlList = []; let imgUrlList = [];
let audioUrlList = []; let audioUrlList = [];
// this.data.audios.forEach((cube) => {
// audioUrlList.push(cube); this.data.carrots.forEach((carrot) => {
// }); imgUrlList.push(carrot.carrotImg);
imgUrlList.push(carrot.holeImg);
});
this.data.targets.forEach((target) => {
imgUrlList.push(target.pic_url);
});
// audioUrlList.push(this.data.wholeAuido); // audioUrlList.push(this.data.wholeAuido);
// audioUrlList.push(this.data.tittle.audio); // audioUrlList.push(this.data.tittle.audio);
...@@ -85,25 +90,35 @@ export class MyGame extends Game { ...@@ -85,25 +90,35 @@ export class MyGame extends Game {
initBg() { initBg() {
this.removeChild(this.getChildByName('bg')); this.removeChild(this.getChildByName('bg'));
this.getFullScaleXY();
let screenSize = this.getScreenSize(); let screenSize = this.getScreenSize();
let defaultSize = this.getDefaultScreenSize(); let defaultSize = this.getDefaultScreenSize();
let bgSized = new TouchSprite(); let bgSized1 = new TouchSprite();
bgSized.init(this.images.get('Img_bg')); bgSized1.init(this.images.get('Img_bg'));
bgSized.anchorX = 0.5; bgSized1.anchorX = 0.5;
bgSized.anchorY = 1; bgSized1.anchorY = 1;
bgSized.setPosition(this._parent.canvasWidth / 2, this._parent.canvasHeight); bgSized1.setPosition(screenSize.width / 2, screenSize.height);
bgSized.setScaleXY(this.getFullScaleXY()); bgSized1.setScaleXY(this.getFullScaleXY());
this.addChild(bgSized); this.addChild(bgSized1);
let bgSized2 = new TouchSprite();
bgSized2.init(this.images.get('Img_bg_ground'));
bgSized2.anchorX = 0.5;
bgSized2.anchorY = 1;
bgSized2.setPosition(screenSize.width / 2, screenSize.height);
bgSized2.scaleX = screenSize.width / defaultSize.width;
bgSized2.scaleY = screenSize.height / defaultSize.height;
this.addChild(bgSized2);
// 背景 // 背景
let bg = new TouchSprite(); let bg = new TouchSprite();
bg.init(this.images.get('Img_bg')) bg.init(this.images.get('Img_bg2'))
bg.setPosition(this._parent.canvasWidth / 2, this._parent.canvasHeight / 2);
bg.setScaleXY(this._parent.mapScale); bg.setScaleXY(this._parent.mapScale);
bg.alpha = 0.5; bg.alpha = 0;
bg.anchorX = 0.5;
bg.anchorY = 1;
bg.setPosition(screenSize.width / 2, screenSize.height);
bg.setName('bg'); bg.setName('bg');
this.addChild(bg); this.addChild(bg);
...@@ -112,7 +127,7 @@ export class MyGame extends Game { ...@@ -112,7 +127,7 @@ export class MyGame extends Game {
const bgRect = new ShapeRect(); const bgRect = new ShapeRect();
bgRect.setSize(57, 65); bgRect.setSize(57, 65);
bgRect.fillColor = '#f8c224'; bgRect.fillColor = '#f8c224';
const sx = this._parent.canvasWidth / this._parent.canvasBaseW; const sx = screenSize.width / this._parent.canvasBaseW;
bgRect.setScaleXY(sx); bgRect.setScaleXY(sx);
bgRect.x = 65 * sx; bgRect.x = 65 * sx;
bgRect.alpha = 0; bgRect.alpha = 0;
...@@ -165,14 +180,86 @@ export class MyGame extends Game { ...@@ -165,14 +180,86 @@ export class MyGame extends Game {
} }
initMiddle() { initMiddle() {
// // 创建背景 // 创建洞
// this.createMiddlebg(); this.createHoles();
// // 创建单词 // 创建胡萝卜
// this.createWords(); this.createCarrots();
// // 创建右下角的喇叭 // 创建可选的单词
// this.createSpeaker(); this.createWords();
// // 创建窗口 }
// this.createWindow();
createCarrots() {
let screenSize = this.getScreenSize();
let defaultSize = this.getDefaultScreenSize();
const length = this.data.carrots.length;
this.data.carrots.forEach((carrot, idx) => {
let carrotSp = new TouchSprite();
carrotSp.init(this.images.get(carrot.carrotImg));
carrotSp.setPositionX(this.bg.width / length * (idx - (length - 1) / 2));
carrotSp.setPositionY(-550 * (screenSize.height / defaultSize.height) / this._parent.mapScale);
this.bg.addChild(carrotSp);
});
}
createHoles() {
let screenSize = this.getScreenSize();
let defaultSize = this.getDefaultScreenSize();
const length = this.data.carrots.length;
this.data.carrots.forEach((carrot, idx) => {
let hole = new TouchSprite();
hole.init(this.images.get(carrot.holeImg));
hole.anchorX = 0.5;
hole.anchorY = 0;
hole.setPositionX(this.bg.width / length * (idx - (length - 1) / 2));
hole.setPositionY(-520 * (screenSize.height / defaultSize.height) / this._parent.mapScale);
this.bg.addChild(hole);
});
}
createWords() {
let screenSize = this.getScreenSize();
let defaultSize = this.getDefaultScreenSize();
const length = this.data.carrots.length;
this.data.targets.forEach((targetData, idx) => {
console.log('targetData.rect = ' + JSON.stringify(targetData.rect));
let targetSp = new TouchSprite();
targetSp.init(this.images.get(targetData.pic_url));
targetSp.setPositionX(targetData.x);
// 一通复杂的计算之后得到一个实际的Y坐标
targetSp.setPositionY((targetData.y - defaultSize.height / 2) * (screenSize.height / defaultSize.height) / this._parent.mapScale);
targetSp.addTouchBeganListener(({ x, y }) => {
this.onTargetSpTouchBengan({ x, y }, targetSp, idx);
});
targetSp.addTouchMoveListener(({ x, y }) => {
this.onTargetSpTouchMoved({ x, y }, targetSp, idx);
});
this.bg.addChild(targetSp);
});
}
editMode = true;
onTargetSpTouchBengan({ x, y }, targetSp, idx) {
targetSp.set('touchStartPos', { x, y });
targetSp.set('startPos', { x: targetSp.x, y: targetSp.y });
}
onTargetSpTouchMoved({ x, y }, targetSp, idx) {
if (this.editMode) {
let screenSize = this.getScreenSize();
let defaultSize = this.getDefaultScreenSize();
let spritePosX = targetSp.get('startPos').x + (x - targetSp.get('touchStartPos').x) / this._parent.mapScale;
let spritePosY = targetSp.get('startPos').y + (y - targetSp.get('touchStartPos').y) / this._parent.mapScale;
targetSp.setPositionX(spritePosX);
targetSp.setPositionY(spritePosY);
this.data.targets[idx].x = spritePosX;
// 一通复杂的计算之后得到一个不随分辨率改变的虚拟Y坐标
this.data.targets[idx].y = spritePosY * this._parent.mapScale / (screenSize.height / defaultSize.height) + defaultSize.height / 2;
}
} }
printCurrentStatus() { printCurrentStatus() {
......
const res = [ const res = [
['Img_bg', "assets/play/Img_bg.png"],
// ['bg', "assets/play/bg.jpg"], ['Img_bg2', "assets/play/Img_bg2.png"],
['btn_left', "assets/play/btn_left.png"], ['Img_bg_ground', "assets/play/Img_bg_ground.png"],
['btn_right', "assets/play/btn_right.png"], ['Img_tittleBg', "assets/play/Img_tittleBg.png"],
// ['text_bg', "assets/play/text_bg.png"], ['carrot', "assets/play/carrot.png"],
['hole', "assets/play/hole.png"],
]; ];
const resAudio = [ const resAudio = [
['click', "assets/play/music/click.mp3"], // ['click', "assets/play/music/click.mp3"],
]; ];
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment