import TWEEN from '@tweenjs/tween.js'; class Sprite { x = 0; y = 0; color = ''; radius = 0; alive = false; margin = 0; angle = 0; ctx; constructor(ctx) { 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; children = [this]; 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; } update($event = null) { if (this.visible) { 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; } drawSelf() { if (this.img) { this.ctx.drawImage(this.img, this._offX, this._offY); } } updateChildren() { if (this.children.length <= 0) { return; } for (let i = 0; i < this.children.length; i++) { if (this.children[i] === this) { this.drawSelf(); } else { this.children[i].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; }); } removeChild(child) { const index = this.children.indexOf(child); if (index !== -1) { this.children.splice(index, 1); } } 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 x = this.x + this._offX * this.scaleX; const y = this.y + this._offY * this.scaleY; const width = this.width * this.scaleX; const height = this.height * this.scaleY; return {x, y, width, height}; } } export class Item extends MySprite { baseX; move(targetY, callBack) { const self = this; const tween = new TWEEN.Tween(this) .to({ y: targetY }, 2500) .easing(TWEEN.Easing.Quintic.Out) .onComplete(function() { self.hide(callBack); // if (callBack) { // callBack(); // } }) .start(); } show(callBack = null) { 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(function() { if (callBack) { callBack(); } }) .start(); // Start the tween immediately. } hide(callBack = null) { const tween = new TWEEN.Tween(this) .to({ alpha: 0 }, 800) // .easing(TWEEN.Easing.Quadratic.Out) // Use an easing function to make the animation smooth. .onComplete(function() { if (callBack) { callBack(); } }) .start(); // Start the tween immediately. } shake(id) { if (!this.baseX) { this.baseX = this.x; } const baseX = this.baseX; const baseTime = 50; const sequence = [ {target: {x: baseX + 40 * id}, time: baseTime - 25}, {target: {x: baseX - 20 * id}, time: baseTime}, {target: {x: baseX + 10 * id}, time: baseTime}, {target: {x: baseX - 5 * id}, time: baseTime}, {target: {x: baseX + 2 * id}, time: baseTime}, {target: {x: baseX - 1 * id}, time: baseTime}, {target: {x: baseX}, time: baseTime}, ]; const self = this; function runSequence() { if (self['shakeTween']) { self['shakeTween'].stop(); } const tween = new TWEEN.Tween(self); if (sequence.length > 0) { // console.log('sequence.length: ', sequence.length); const action = sequence.shift(); tween.to(action['target'], action['time']); tween.onComplete( () => { runSequence(); }); tween.start(); self['shakeTween'] = tween; } } runSequence(); } drop(targetY, callBack = null) { const self = this; const time = Math.abs(targetY - this.y) * 2.4; this.alpha = 1; const tween = new TWEEN.Tween(this) .to({ y: targetY }, time) .easing(TWEEN.Easing.Cubic.In) .onComplete(function() { // self.hideItem(callBack); if (callBack) { callBack(); } }) .start(); } } export class EndSpr extends MySprite { show(s) { this.scaleX = this.scaleY = 0; this.alpha = 0; const tween = new TWEEN.Tween(this) .to({ alpha: 1, scaleX: s, scaleY: s }, 800) .easing(TWEEN.Easing.Elastic.Out) // Use an easing function to make the animation smooth. .onComplete(function() { }) .start(); // Start the tween immediately. } } 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 HotZoneItem extends MySprite { lineDashFlag = false; arrow: MySprite; label: Label; text; arrowTop; arrowRight; setSize(w, h) { this.width = w; this.height = h; const rect = new ShapeRect(this.ctx); rect.x = -w / 2; rect.y = -h / 2; rect.setSize(w, h); rect.fillColor = '#ffffff'; rect.alpha = 0.2; this.addChild(rect); } showLabel(text = null) { if (!this.label) { this.label = new Label(this.ctx); this.label.anchorY = 0; this.label.fontSize = '40px'; this.label.textAlign = 'center'; this.addChild(this.label); // this.label.scaleX = 1 / this.scaleX; // this.label.scaleY = 1 / this.scaleY; this.refreshLabelScale(); } if (text) { this.label.text = text; } else if (this.text) { this.label.text = this.text; } this.label.visible = true; } hideLabel() { if (!this.label) { return; } this.label.visible = false; } refreshLabelScale() { if (this.scaleX == this.scaleY) { this.label.setScaleXY(1); } if (this.scaleX > this.scaleY) { this.label.scaleX = this.scaleY / this.scaleX; } else { this.label.scaleY = this.scaleX / this.scaleY; } } showLineDash() { this.lineDashFlag = true; if (this.arrow) { this.arrow.visible = true; } else { this.arrow = new MySprite(this.ctx); this.arrow.load('assets/common/arrow.png', 1, 0); this.arrow.setScaleXY(0.06); this.arrowTop = new MySprite(this.ctx); this.arrowTop.load('assets/common/arrow_top.png', 0.5, 0); this.arrowTop.setScaleXY(0.06); this.arrowRight = new MySprite(this.ctx); this.arrowRight.load('assets/common/arrow_right.png', 1, 0.5); this.arrowRight.setScaleXY(0.06); } this.showLabel(); } hideLineDash() { this.lineDashFlag = false; if (this.arrow) { this.arrow.visible = false; } this.hideLabel(); } drawArrow() { if (!this.arrow) { return; } const rect = this.getBoundingBox(); this.arrow.x = rect.x + rect.width; this.arrow.y = rect.y; this.arrow.update(); this.arrowTop.x = rect.x + rect.width / 2; this.arrowTop.y = rect.y; this.arrowTop.update(); this.arrowRight.x = rect.x + rect.width; this.arrowRight.y = rect.y + rect.height / 2; this.arrowRight.update(); } drawFrame() { this.ctx.save(); const rect = this.getBoundingBox(); const w = rect.width; const h = rect.height; const x = rect.x + w / 2; const y = rect.y + h / 2; this.ctx.setLineDash([5, 5]); this.ctx.lineWidth = 2; this.ctx.strokeStyle = '#1bfff7'; // this.ctx.fillStyle = '#ffffff'; this.ctx.beginPath(); this.ctx.moveTo( x - w / 2, y - h / 2); this.ctx.lineTo(x + w / 2, y - h / 2); this.ctx.lineTo(x + w / 2, y + h / 2); this.ctx.lineTo(x - w / 2, y + h / 2); this.ctx.lineTo(x - w / 2, y - h / 2); // this.ctx.fill(); this.ctx.stroke(); this.ctx.restore(); } draw() { super.draw(); if (this.lineDashFlag) { this.drawFrame(); this.drawArrow(); } } } export class EditorItem extends MySprite { lineDashFlag = false; arrow: MySprite; label:Label; text; showLabel(text = null) { if (!this.label) { this.label = new Label(this.ctx); this.label.anchorY = 0; this.label.fontSize = '50px'; this.label.textAlign = 'center'; this.addChild(this.label); this.label.setScaleXY(1 / this.scaleX); } if (text) { this.label.text = text; } else if (this.text) { this.label.text = this.text; } this.label.visible = true; } hideLabel() { if (!this.label) { return; } this.label.visible = false; } showLineDash() { this.lineDashFlag = true; if (this.arrow) { this.arrow.visible = true; } else { this.arrow = new MySprite(this.ctx); this.arrow.load('assets/common/arrow.png', 1, 0); this.arrow.setScaleXY(0.06); } this.showLabel(); } hideLineDash() { this.lineDashFlag = false; if (this.arrow) { this.arrow.visible = false; } this.hideLabel(); } drawArrow() { if (!this.arrow) { return; } const rect = this.getBoundingBox(); this.arrow.x = rect.x + rect.width; this.arrow.y = rect.y; this.arrow.update(); } drawFrame() { this.ctx.save(); const rect = this.getBoundingBox(); const w = rect.width; const h = rect.height; const x = rect.x + w / 2; const y = rect.y + h / 2; this.ctx.setLineDash([5, 5]); this.ctx.lineWidth = 2; this.ctx.strokeStyle = '#1bfff7'; // this.ctx.fillStyle = '#ffffff'; this.ctx.beginPath(); this.ctx.moveTo( x - w / 2, y - h / 2); this.ctx.lineTo(x + w / 2, y - h / 2); this.ctx.lineTo(x + w / 2, y + h / 2); this.ctx.lineTo(x - w / 2, y + h / 2); this.ctx.lineTo(x - w / 2, y - h / 2); // this.ctx.fill(); this.ctx.stroke(); this.ctx.restore(); } draw() { super.draw(); if (this.lineDashFlag) { this.drawFrame(); this.drawArrow(); } } } export class Label extends MySprite { text:String; fontSize:String = '40px'; fontName:String = 'Verdana'; textAlign:String = 'left'; constructor(ctx) { super(ctx); this.init(); } drawText() { // console.log('in drawText', this.text); if (!this.text) { return; } this.ctx.font = `${this.fontSize} ${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); } drawSelf() { super.drawSelf(); this.drawText(); } } 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 = p2x - p1x; // const _y = p2y - p1y; // const tan = _y / _x; // // const radina = Math.atan(tan); // 用反三角函数求弧度 // const angle = Math.floor(180 / (Math.PI / radina)); // // // console.log('r: ' , angle); // return angle; // const x = Math.abs(px - mx); const y = Math.abs(py - my); // const x = Math.abs(mx - px); // const y = Math.abs(my - py); 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; }