import {Component, ElementRef, ViewChild, OnInit, Input, OnDestroy, HostListener} from '@angular/core'; import { Label, MySprite, tweenChange, } from './Unit'; import {res, resAudio} from './resources'; import {Subject} from 'rxjs'; import {debounceTime} from 'rxjs/operators'; import TWEEN from '@tweenjs/tween.js'; @Component({ selector: 'app-play', templateUrl: './play.component.html', styleUrls: ['./play.component.css'] }) export class PlayComponent implements OnInit, OnDestroy { @ViewChild('canvas', {static: true }) canvas: ElementRef; @ViewChild('wrap', {static: true }) wrap: ElementRef; // 数据 data; ctx; canvasWidth = 1280; // canvas实际宽度 canvasHeight = 720; // canvas实际高度 canvasBaseW = 1280; // canvas 资源预设宽度 canvasBaseH = 720; // canvas 资源预设高度 mx; // 点击x坐标 my; // 点击y坐标 // 资源 rawImages = new Map(res); rawAudios = new Map(resAudio); images = new Map(); animationId: any; winResizeEventStream = new Subject(); audioObj = {}; renderArr; mapScale = 1; canvasLeft; canvasTop; saveKey = 'test_0011'; btnLeft; btnRight; pic1; pic2; canTouch = true; curPic; @HostListener('window:resize', ['$event']) onResize(event) { this.winResizeEventStream.next(); } ngOnInit() { this.data = {}; // 获取数据 const getData = (<any> window).courseware.getData; getData((data) => { if (data && typeof data == 'object') { this.data = data; } // console.log('data:' , data); // 初始化 各事件监听 this.initListener(); // 若无数据 则为预览模式 需要填充一些默认数据用来显示 this.initDefaultData(); // 初始化 音频资源 this.initAudio(); // 初始化 图片资源 this.initImg(); // 开始预加载资源 this.load(); }, this.saveKey); } ngOnDestroy() { window['curCtx'] = null; window.cancelAnimationFrame(this.animationId); } load() { // 预加载资源 this.loadResources().then(() => { window["air"].hideAirClassLoading(this.saveKey, this.data); this.init(); this.update(); }); } init() { this.initCtx(); this.initData(); this.initView(); } initCtx() { this.canvasWidth = this.wrap.nativeElement.clientWidth; this.canvasHeight = this.wrap.nativeElement.clientHeight; this.canvas.nativeElement.width = this.wrap.nativeElement.clientWidth; this.canvas.nativeElement.height = this.wrap.nativeElement.clientHeight; this.ctx = this.canvas.nativeElement.getContext('2d'); this.canvas.nativeElement.width = this.canvasWidth; this.canvas.nativeElement.height = this.canvasHeight; window['curCtx'] = this.ctx; } updateItem(item) { if (item) { item.update(); } } updateArr(arr) { if (!arr) { return; } for (let i = 0; i < arr.length; i++) { arr[i].update(this); } } initListener() { this.winResizeEventStream .pipe(debounceTime(500)) .subscribe(data => { this.renderAfterResize(); }); // --------------------------------------------- const setParentOffset = () => { const rect = this.canvas.nativeElement.getBoundingClientRect(); this.canvasLeft = rect.left; this.canvasTop = rect.top; }; const setMxMyByTouch = (event) => { if (event.touches.length <= 0) { return; } if (this.canvasLeft == null) { setParentOffset(); } this.mx = event.touches[0].pageX - this.canvasLeft; this.my = event.touches[0].pageY - this.canvasTop; }; const setMxMyByMouse = (event) => { this.mx = event.offsetX; this.my = event.offsetY; }; // --------------------------------------------- let firstTouch = true; const touchDownFunc = (e) => { if (firstTouch) { firstTouch = false; removeMouseListener(); } setMxMyByTouch(e); this.mapDown(e); }; const touchMoveFunc = (e) => { setMxMyByTouch(e); this.mapMove(e); }; const touchUpFunc = (e) => { setMxMyByTouch(e); this.mapUp(e); }; const mouseDownFunc = (e) => { if (firstTouch) { firstTouch = false; removeTouchListener(); } setMxMyByMouse(e); this.mapDown(e); }; const mouseMoveFunc = (e) => { setMxMyByMouse(e); this.mapMove(e); }; const mouseUpFunc = (e) => { setMxMyByMouse(e); this.mapUp(e); }; const element = this.canvas.nativeElement; const addTouchListener = () => { element.addEventListener('touchstart', touchDownFunc); element.addEventListener('touchmove', touchMoveFunc); element.addEventListener('touchend', touchUpFunc); element.addEventListener('touchcancel', touchUpFunc); }; const removeTouchListener = () => { element.removeEventListener('touchstart', touchDownFunc); element.removeEventListener('touchmove', touchMoveFunc); element.removeEventListener('touchend', touchUpFunc); element.removeEventListener('touchcancel', touchUpFunc); }; const addMouseListener = () => { element.addEventListener('mousedown', mouseDownFunc); element.addEventListener('mousemove', mouseMoveFunc); element.addEventListener('mouseup', mouseUpFunc); }; const removeMouseListener = () => { element.removeEventListener('mousedown', mouseDownFunc); element.removeEventListener('mousemove', mouseMoveFunc); element.removeEventListener('mouseup', mouseUpFunc); }; addMouseListener(); addTouchListener(); } playAudio(key, now = false, callback = null) { const audio = this.audioObj[key]; if (audio) { if (now) { audio.pause(); audio.currentTime = 0; } if (callback) { audio.onended = () => { callback(); }; } audio.play(); } } loadResources() { const pr = []; this.rawImages.forEach((value, key) => {// 预加载图片 const p = this.preload(value) .then(img => { this.images.set(key, img); }) .catch(err => console.log(err)); pr.push(p); }); this.rawAudios.forEach((value, key) => {// 预加载音频 const a = this.preloadAudio(value) .then(() => { // this.images.set(key, img); }) .catch(err => console.log(err)); pr.push(a); }); return Promise.all(pr); } preload(url) { return new Promise((resolve, reject) => { const img = new Image(); // img.crossOrigin = "anonymous"; img.onload = () => resolve(img); img.onerror = reject; img.src = url; }); } preloadAudio(url) { return new Promise((resolve, reject) => { const audio = new Audio(); audio.oncanplay = (a) => { resolve(); }; audio.onerror = () => { reject(); }; audio.src = url; audio.load(); }); } renderAfterResize() { this.canvasWidth = this.wrap.nativeElement.clientWidth; this.canvasHeight = this.wrap.nativeElement.clientHeight; this.init(); } checkClickTarget(target) { const rect = target.getBoundingBox(); if (this.checkPointInRect(this.mx, this.my, rect)) { return true; } return false; } getWorlRect(target) { let rect = target.getBoundingBox(); if (target.parent) { const pRect = this.getWorlRect(target.parent); rect.x += pRect.x; rect.y += pRect.y; } return rect; } checkPointInRect(x, y, rect) { if (x >= rect.x && x <= rect.x + rect.width) { if (y >= rect.y && y <= rect.y + rect.height) { return true; } } return false; } addUrlToAudioObj(key, url = null, vlomue = 1, loop = false, callback = null) { const audioObj = this.audioObj; if (url == null) { url = key; } this.rawAudios.set(key, url); const audio = new Audio(); audio.src = url; audio.load(); audio.loop = loop; audio.volume = vlomue; audioObj[key] = audio; if (callback) { audio.onended = () => { callback(); }; } } addUrlToImages(url) { this.rawImages.set(url, url); } // ======================================================编写区域========================================================================== /** * 添加默认数据 便于无数据时的展示 */ initDefaultData() { if (!this.data.pic_url) { this.data.pic_url = 'assets/play/default/pic.jpg'; this.data.pic_url_2 = 'assets/play/default/pic.jpg'; } } /** * 添加预加载图片 */ initImg() { this.addUrlToImages(this.data.pic_url); this.addUrlToImages(this.data.pic_url_2); } /** * 添加预加载音频 */ initAudio() { // 音频资源 this.addUrlToAudioObj(this.data.audio_url); this.addUrlToAudioObj(this.data.audio_url_2); // 音效 this.addUrlToAudioObj('click', this.rawAudios.get('click'), 0.3); } /** * 初始化数据 */ initData() { const sx = this.canvasWidth / this.canvasBaseW; const sy = this.canvasHeight / this.canvasBaseH; const s = Math.min(sx, sy); this.mapScale = s; // this.mapScale = sx; // this.mapScale = sy; this.renderArr = []; } /** * 初始化试图 */ initView() { this.initPic(); this.initBottomPart(); } initBottomPart() { const btnLeft = new MySprite(); btnLeft.init(this.images.get('btn_left')); btnLeft.x = this.canvasWidth - 150 * this.mapScale; btnLeft.y = this.canvasHeight - 100 * this.mapScale; btnLeft.setScaleXY(this.mapScale); this.renderArr.push(btnLeft); this.btnLeft = btnLeft; const btnRight = new MySprite(); btnRight.init(this.images.get('btn_right')); btnRight.x = this.canvasWidth - 50 * this.mapScale; btnRight.y = this.canvasHeight - 100 * this.mapScale; btnRight.setScaleXY(this.mapScale); this.renderArr.push(btnRight); this.btnRight = btnRight; } initPic() { const maxW = this.canvasWidth * 0.7; const pic1 = new MySprite(); pic1.init(this.images.get(this.data.pic_url)); pic1.x = this.canvasWidth / 2; pic1.y = this.canvasHeight / 2; pic1.setScaleXY(maxW / pic1.width); this.renderArr.push(pic1); this.pic1 = pic1; const label1 = new Label(); label1.text = this.data.text; label1.textAlign = 'center'; label1.fontSize = 50; label1.fontName = 'BRLNSDB'; label1.fontColor = '#ffffff'; pic1.addChild(label1); const pic2 = new MySprite(); pic2.init(this.images.get(this.data.pic_url_2)); pic2.x = this.canvasWidth / 2 + this.canvasWidth; pic2.y = this.canvasHeight / 2; pic2.setScaleXY(maxW / pic2.width); this.renderArr.push(pic2); this.pic2 = pic2; this.curPic = pic1; } btnLeftClicked() { this.lastPage(); } btnRightClicked() { this.nextPage(); } lastPage() { if (this.curPic == this.pic1) { return; } this.canTouch = false; const moveLen = this.canvasWidth; tweenChange(this.pic1, {x: this.pic1.x + moveLen}, 1); tweenChange(this.pic2, {x: this.pic2.x + moveLen}, 1, () => { this.canTouch = true; this.curPic = this.pic1; }); } nextPage() { if (this.curPic == this.pic2) { return; } this.canTouch = false; const moveLen = this.canvasWidth; tweenChange(this.pic1, {x: this.pic1.x - moveLen}, 1); tweenChange(this.pic2, {x: this.pic2.x - moveLen}, 1, () => { this.canTouch = true; this.curPic = this.pic2; }); } pic1Clicked() { this.playAudio(this.data.audio_url); } pic2Clicked() { this.playAudio(this.data.audio_url_2); } mapDown(event) { if (!this.canTouch) { return; } if ( this.checkClickTarget(this.btnLeft) ) { this.btnLeftClicked(); return; } if ( this.checkClickTarget(this.btnRight) ) { this.btnRightClicked(); return; } if ( this.checkClickTarget(this.pic1) ) { this.pic1Clicked(); return; } if ( this.checkClickTarget(this.pic2) ) { this.pic2Clicked(); return; } } mapMove(event) { } mapUp(event) { } update() { // ---------------------------------------------------------- this.animationId = window.requestAnimationFrame(this.update.bind(this)); // 清除画布内容 this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight); // tween 更新动画 TWEEN.update(); // ---------------------------------------------------------- this.updateArr(this.renderArr); } }