Commit 1ba521e9 authored by limingzhe's avatar limingzhe

feat: 首次提交

parent 9f3a2900
......@@ -128,5 +128,8 @@
}
}
},
"defaultProject": "ng-template-generator"
"defaultProject": "ng-template-generator",
"cli": {
"analytics": false
}
}
\ No newline at end of file
......@@ -24,6 +24,9 @@ import {AudioRecorderComponent} from './common/audio-recorder/audio-recorder.com
import { FontAwesomeModule, FaIconLibrary } from '@fortawesome/angular-fontawesome';
import { fas } from '@fortawesome/free-solid-svg-icons';
import { far } from '@fortawesome/free-regular-svg-icons';
import { CustomActionComponent } from './common/custom-action/custom-action.component';
import { UploadDragonBoneComponent } from './common/upload-dragon-bone/upload-dragon-bone.component';
import { SubTemplateComponent } from './common/sub-template/sub-template.component';
registerLocaleData(zh);
......@@ -40,8 +43,11 @@ registerLocaleData(zh);
TimePipe,
UploadVideoComponent,
CustomHotZoneComponent,
SubTemplateComponent,
PlayerContentWrapperComponent
PlayerContentWrapperComponent,
CustomActionComponent,
UploadDragonBoneComponent
],
imports: [
......
......@@ -35,7 +35,8 @@
<ng-template #truthyTemplate >
<div class="btn-delete" (click)="onBtnDeleteAudio()">
<fa-icon icon="close"></fa-icon>
<!-- <fa-icon icon="close"></fa-icon> -->
<i nz-icon nzType="close" nzTheme="outline"></i>
</div>
</ng-template>
......
......@@ -26,7 +26,7 @@ export class AudioRecorderComponent implements OnInit, OnChanges, OnDestroy {
uploadData;
@Input()
needRemove = false;
needRemove = true;
@Input()
audioItem: any = null;
......@@ -62,8 +62,25 @@ export class AudioRecorderComponent implements OnInit, OnChanges, OnDestroy {
this.uploadUrl = url;
this.uploadData = data;
};
this.setUploadUrl();
}
setUploadUrl() {
if (!this.uploadUrl) {
this.uploadUrl = (<any> window).courseware.uploadUrl();
this.uploadData = (<any> window).courseware.uploadData();
setTimeout(() => {
this.setUploadUrl();
}, 500);
}
}
init() {
this.playIcon = 'play';
this.isPlaying = false;
......@@ -157,6 +174,7 @@ export class AudioRecorderComponent implements OnInit, OnChanges, OnDestroy {
onBtnDeleteAudio() {
this.audioUrl = null;
this.audioUploaded.emit({});
this.audioRemoved.emit();
}
......
<div class="position-relative">
<button nz-button (click)="setAnimaBtnClick()" style=" border-radius: 0.5rem; border: 1px solid #ddd;" >
<i nz-icon nzType="tool" nzTheme="outline"></i>
{{btnName}}
</button>
<!--配置龙骨面板-->
<nz-modal [(nzVisible)]="panelVisible" (nzAfterClose)="closePanel()" [nzTitle]="btnName" (nzOnCancel)="panelCancel()" (nzOnOk)="panelOk()" nzOkText="保存">
<div>
<h4>基础内容:</h4>
<div style="margin-bottom: 10px; width: 80%; margin: auto;">
<!-- <nz-radio-group [ngModel]="item.type" (ngModelChange)="radioChange($event)">
<label nz-radio nzValue="pic">图片</label>
<label nz-radio nzValue="text">文本</label>
</nz-radio-group> -->
<div *ngIf="item.type == 'pic'">
<app-upload-image-with-preview
[picUrl]="item?.pic_url"
(imageUploaded)="onItemImgUploadSuccess($event, item)">
</app-upload-image-with-preview>
</div>
<div *ngIf="item.type == 'text'">
<input type="text" nz-input [(ngModel)]="item.text" (blur)="saveText(item)">
</div>
<div *ngIf="item.type == 'anima'" style="margin-left: 100px;">
<app-upload-dragon-bone
[skeJsonData]=item.skeJsonData
[texJsonData]=item.texJsonData
[texPngData]=item.texPngData
(save)="saveAnima($event)"
>
</app-upload-dragon-bone>
</div>
</div>
<nz-divider style="margin-top: 10px;"></nz-divider>
<h4>状态设置:</h4>
<div style="display: flex; justify-content: space-around;">
<div style="width: 30%; display: flex; align-items: center; flex-direction: column;">
<h5> 开始状态 </h5>
<div *ngFor="let op of item.changeOption; let i = index" style="margin-bottom: 5px; ">
<div style="display: flex; align-items: center; justify-content: center;">
<span>{{op[0]}}: </span> <input type="text" nz-input [(ngModel)]="op[1]" (blur)="saveText(op)" style="margin-left: 5px;">
</div>
</div>
</div>
<div style="width: 30%; display: flex; align-items: center; flex-direction: column; ">
<h5> 切换时间 </h5>
<div style="display: flex; width: 100%; align-items: center; justify-content: center;">
<span>time: </span> <input type="text" nz-input [(ngModel)]="item.changeTime" (blur)="saveText(item.changeTime)" style="width: 80px; margin-left: 5px;">
</div>
</div>
<div style="width: 30%; display: flex; align-items: center; flex-direction: column;">
<h5> 结束状态 </h5>
<div *ngFor="let op of item.changeOption; let i = index" style="margin-bottom: 5px; ">
<div style="display: flex; align-items: center; justify-content: center;">
<span>{{op[0]}}: </span> <input [disabled]="op[2] == null" type="text" nz-input [(ngModel)]="op[2]" (blur)="saveText(op)" style="margin-left: 5px;">
</div>
</div>
</div>
</div>
<div style="display: flex; align-items: center; justify-content: center; margin-top: 10px;">
<button style="margin-left: 20px; margin-top: -5px" nz-button (click)="copyChangeData()"> 复制数据 </button>
<div style="margin-left: 20px; margin-top: -5px" >
<span>粘贴数据: </span>
<input style="width: 100px;" type="text" nz-input [(ngModel)]="pasteData" >
<button style="margin-left: 5px;" nz-button [disabled]="pasteData==''" nzType="primary" (click)="importData()">导入</button>
</div>
</div>
<nz-divider style="margin-top: 10px;"></nz-divider>
<h4> 音频设置 </h4>
<app-audio-recorder
style="margin: auto"
[audioUrl]="item?.audio_url"
(audioUploaded)="onItemAudioUploadSuccess($event, item)"
></app-audio-recorder>
</div>
<!--
<div class="anima-upload-btn">
<span style="margin-right: 10px">上传 ske_json 文件: </span>
<nz-upload [nzShowUploadList]="false"
nzAccept="application/json"
[nzAction]="uploadUrl"
[nzData]="uploadData"
(nzChange)="skeJsonHandleChange($event)">
<button nz-button><i nz-icon nzType="upload"></i><span>Upload</span></button>
</nz-upload>
<i *ngIf="isSkeJsonLoading" style="margin-left: 10px;" nz-icon [nzType]="'loading'"></i>
<span *ngIf="skeJsonData && skeJsonData['name']" style="margin-left: 10px"><u> {{skeJsonData['name']}} </u></span>
</div>
<div class="anima-upload-btn">
<span style="margin-right: 10px">上传 tex_json 文件: </span>
<nz-upload [nzShowUploadList]="false"
nzAccept="application/json"
[nzAction]="uploadUrl"
[nzData]="uploadData"
(nzChange)="texJsonHandleChange($event)">
<button nz-button><i nz-icon nzType="upload"></i><span>Upload</span></button>
</nz-upload>
<i *ngIf="isTexJsonLoading" style="margin-left: 10px;" nz-icon [nzType]="'loading'"></i>
<span *ngIf="texJsonData && texJsonData['name']" style="margin-left: 10px"><u> {{texJsonData['name']}} </u></span>
</div>
<div class="anima-upload-btn">
<span style="margin-right: 10px">上传 tex_png 文件: </span>
<nz-upload [nzShowUploadList]="false"
nzAccept = "image/*"
[nzAction]="uploadUrl"
[nzData]="uploadData"
(nzChange)="texPngHandleChange($event)">
<button nz-button><i nz-icon nzType="upload"></i><span>Upload</span></button>
</nz-upload>
<i *ngIf="isTexPngLoading" style="margin-left: 10px;" nz-icon [nzType]="'loading'"></i>
<span *ngIf="texPngData && texPngData['name']" style="margin-left: 10px"><u> {{texPngData['name']}} </u></span>
</div>
<div class="anima-upload-btn" *ngIf="animaNames && animaNames.length > 0">
提示:需包含动画: {{animaNames.toString()}}.
</div> -->
</nz-modal>
</div>
@import '../../style/common_mixin.css';
.p-image-uploader {
position: relative;
display: block;
width: 100%;
height: 0;
padding-bottom: 56.25%;
.p-box {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
border: 2px dashed #ddd;
border-radius: 0.5rem;
background-color: #fafafa;
text-align: center;
color: #aaa;
.p-upload-icon {
text-align: center;
margin: auto;
.anticon-upload {
color: #888;
font-size: 5rem;
}
.p-progress-bar {
position: relative;
width: 20rem;
height: 1.5rem;
border: 1px solid #ccc;
border-radius: 1rem;
.p-progress-bg {
background-color: #1890ff;
border-radius: 1rem;
height: 100%;
}
.p-progress-value {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
text-shadow: 0 0 4px #000;
color: white;
text-align: center;
font-size: 0.9rem;
line-height: 1.5rem;
}
}
}
.p-preview {
width: 100%;
height: 100%;
background-size: contain;
background-repeat: no-repeat;
background-position: 50% 50%;
//background-image: url("https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png");
}
}
.d-flex{
display: flex;
}
}
.p-btn-delete {
position: absolute;
right: -0.5rem;
top: -0.5rem;
width: 2rem;
height: 2rem;
border: 0.2rem solid white;
border-radius: 50%;
font-size: 1.2rem;
background-color: #fb781a;
color: white;
text-align: center;
}
.p-upload-progress-bg {
position: absolute;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
& .i-text {
position: absolute;
text-align: center;
color: white;
text-shadow: 0 0 2px rgba(0, 0, 0, .85);
}
& .i-bg {
position: absolute;
left: 0;
top: 0;
height: 100%;
background-color: #27b43f;
border-radius: 0.5rem;
}
}
.anima-upload-btn {
padding: 10px;
}
:host ::ng-deep .ant-upload {
display: block;
}
import {ApplicationRef, Component, EventEmitter, Input, OnChanges, OnDestroy, Output} from '@angular/core';
import { NzMessageService, UploadXHRArgs, UploadFile } from 'ng-zorro-antd';
@Component({
selector: 'app-custom-action',
templateUrl: './custom-action.component.html',
styleUrls: ['./custom-action.component.scss']
})
export class CustomActionComponent implements OnDestroy, OnChanges {
uploading = false;
progress = 0;
@Input()
btnName = '配置变化状态';
@Input()
option = []
@Input()
item;
@Input()
type = 'text'
@Output()
save = new EventEmitter();
@Output()
refreshEmitter = new EventEmitter();
uploadUrl;
uploadData;
panelVisible = false;
pasteData = '';
constructor(private appRef: ApplicationRef, private nzMessageService: NzMessageService) {
window['air'].getUploadCallback = (url, data) => {
this.uploadUrl = url;
this.uploadData = data;
};
const tmpId = setInterval(() => {
console.log(' in setInterval')
this.uploadUrl = (<any> window).courseware.uploadUrl();
this.uploadData = (<any> window).courseware.uploadData();
if (this.uploadUrl) {
clearInterval(tmpId);
}
}, 500)
}
ngOnInit() {
this.initItem();
}
ngOnChanges() {
}
initItem() {
console.log('```this.item: ', this.item);
if (!this.item) {
this.item = {
type: this.type,
skeJsonData: {},
texJsonData: {},
texPngData: {},
changeStart: [
],
changeEnd: [
],
changeTime: 0.3
};
if (this.option) {
this.item.changeOption = JSON.parse(JSON.stringify(this.option));
}
}
console.log('initItem', this.item);
}
setAnimaBtnClick() {
this.panelVisible = true;
this.refresh();
}
panelCancel() {
this.panelVisible = false;
this.refresh();
}
panelOk() {
// this.sendItemDragonBoneData();
this.panelVisible = false;
this.refresh();
this.save.emit(this.item);
}
saveText(t) {
}
radioChange(e) {
this.item.type = e;
console.log('e: ', e);
}
onItemImgUploadSuccess(e, item) {
item.pic_url = e.url;
this.refresh();
}
onItemAudioUploadSuccess(e, item) {
item.audio_url = e.url;
this.refresh();
}
saveAnima(e) {
console.log("~ e: ", e);
}
copyChangeData() {
console.log('this.item: ', this.item);
const jsonData = {
changeOption: this.item.changeOption,
changeTime : this.item.changeTime
// bgItem,
// hotZoneItemArr,
// isHasRect: this.isHasRect,
// isHasPic: this.isHasPic,
// isHasText: this.isHasText,
// isHasAudio: this.isHasAudio,
// isHasAnima: this.isHasAnima,
// hotZoneFontObj: this.hotZoneFontObj,
// defaultItemType: this.defaultItemType,
// hotZoneImgSize: this.hotZoneImgSize,
};
const oInput = document.createElement('input');
oInput.value = JSON.stringify(jsonData);
document.body.appendChild(oInput);
oInput.select(); // 选择对象
document.execCommand('Copy'); // 执行浏览器复制命令
document.body.removeChild(oInput);
this.nzMessageService.success('复制成功');
}
importData() {
if (!this.pasteData) {
return;
}
try {
const data = JSON.parse(this.pasteData);
console.log('data:', data);
const {
changeOption,
changeTime
} = data;
this.item.changeOption = changeOption;
this.item.changeTime = changeTime;
this.pasteData = '';
} catch (e) {
console.log('err: ', e);
this.nzMessageService.error('导入失败');
}
}
/**
* 刷新 渲染页面
*/
refresh() {
// this.refreshEmitter.emit();
setTimeout(() => {
this.appRef.tick();
}, 1);
}
closePanel() {
console.log(' in closePanel ');
this.refresh();
}
ngOnDestroy() {
}
}
......@@ -329,6 +329,7 @@ export class MySprite extends Sprite {
this.scaleX = this.scaleY = value;
}
getBoundingBox() {
const getParentData = (item) => {
......@@ -1956,6 +1957,10 @@ export class HotZoneItem extends MySprite {
audio_url;
pic_url;
text;
gIdx;
isAnimaStyle = false;
private _itemType;
private shapeRect: ShapeRect;
......@@ -2056,7 +2061,10 @@ export class HotZoneItem extends MySprite {
this.hideLabel();
}
setAnimaStyle(isAnimaStyle) {
this.isAnimaStyle = isAnimaStyle;
console.log('in setAnimaStyle ')
}
drawArrow() {
if (!this.arrow) { return; }
......@@ -2068,6 +2076,7 @@ export class HotZoneItem extends MySprite {
this.arrow.update();
if (!this.isAnimaStyle) {
this.arrowTop.x = rect.x + rect.width / 2;
this.arrowTop.y = rect.y;
this.arrowTop.update();
......@@ -2077,6 +2086,8 @@ export class HotZoneItem extends MySprite {
this.arrowRight.update();
}
}
drawFrame() {
......@@ -2197,10 +2208,155 @@ export class HotZoneLabel extends Label {
this.drawFrame();
}
getLabelRect() {
const rect = this.getBoundingBox();
const width = rect.width / this.scaleX;
const height = this.height * this.scaleY;
const x = this.x - width / 2;
const y = this.y - height / 2;
return {width, height, x, y};
}
}
export class HotZoneAction extends MySprite {
}
export class DragItem extends MySprite {
lineDashFlag = true;
item;
init() {
this.anchorX = 0.5;
this.anchorY = 0.5;
this.initCenterCircle();
}
setSize(w, h) {
this.anchorX = 0.5;
this.anchorY = 0.5;
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 = '#000000';
// rect.alpha = 0.1;
// this.addChild(rect);
}
initCenterCircle() {
const circle = new ShapeCircle(this.ctx);
circle.setRadius(10);
circle.fillColor = '#ffa568'
this.addChild(circle);
this.width = circle.width;
this.height = circle.height;
}
getPosition() {
return {x: this.x, y: this.y};
}
drawLine() {
if (!this.item) {
return;
}
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([4, 4]);
this.ctx.lineWidth = 1;
this.ctx.strokeStyle = '#ffa568';
this.ctx.beginPath();
this.ctx.moveTo( x + w / 2 , y + h / 2 );
this.ctx.lineTo(this.item.x, this.item.y);
// this.ctx.fill();
this.ctx.stroke();
this.ctx.restore();
}
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([4, 4]);
this.ctx.lineWidth = 2;
this.ctx.strokeStyle = '#ffa568';
// 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.drawLine();
// this.drawFrame();
// this.drawArrow();
}
}
}
export class EditorItem extends MySprite {
......@@ -2208,6 +2364,8 @@ export class EditorItem extends MySprite {
arrow: MySprite;
label: Label;
text;
arrowTop;
arrowRight;
showLabel(text = null) {
......@@ -2246,6 +2404,13 @@ export class EditorItem extends MySprite {
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();
......@@ -2262,16 +2427,25 @@ export class EditorItem extends MySprite {
this.hideLabel();
}
refreshLabelScale() {}
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() {
......@@ -2325,784 +2499,3 @@ export class EditorItem extends MySprite {
}
//
//
// 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;
// title;
//
// arrowTop;
// arrowRight;
//
// audio_url;
// pic_url;
// text;
// private _itemType;
// private shapeRect: ShapeRect;
//
// get itemType() {
// return this._itemType;
// }
// set itemType(value) {
// this._itemType = value;
// }
//
// 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.title) {
// this.label.text = this.title;
// }
// 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;
//
// }
......@@ -32,15 +32,106 @@
<nz-divider style="margin-top: 10px;"></nz-divider>
<div style="margin-top: -20px; margin-bottom: 5px; width: 100%;">
<div *ngIf="customTypeGroupArr">
<nz-radio-group [ngModel]="it.gIdx" (ngModelChange)="customRadioChange($event, it)" style="display: flex; align-items: center; justify-content: center; flex-wrap: wrap;">
<div *ngFor="let group of customTypeGroupArr; let gIdx = index" style="display: flex; ">
<label nz-radio nzValue="{{gIdx}}">{{group.name}}</label>
</div>
</nz-radio-group>
</div>
<!-- <div *ngIf="!customTypeGroupArr">
<nz-radio-group [ngModel]="it.itemType" (ngModelChange)="radioChange($event, it)" style="display: flex; align-items: center; justify-content: center">
<div style="margin-top: -20px; margin-bottom: 5px">
<nz-radio-group [ngModel]="it.itemType" (ngModelChange)="radioChange($event, it)">
<label *ngIf="isHasRect" nz-radio nzValue="rect">矩形</label>
<label *ngIf="isHasPic" nz-radio nzValue="pic">图片</label>
<label *ngIf="isHasText" nz-radio nzValue="text">文本</label>
</nz-radio-group>
</div> -->
</div>
<div *ngIf="customTypeGroupArr && customTypeGroupArr[it.gIdx]">
<div *ngIf="customTypeGroupArr[it.gIdx].pic">
<app-upload-image-with-preview
[picUrl]="it?.pic_url"
(imageUploaded)="onItemImgUploadSuccess($event, it)">
</app-upload-image-with-preview>
</div>
<div *ngIf="customTypeGroupArr[it.gIdx].text" style="margin-top: 5px">
<input type="text" nz-input [(ngModel)]="it.text" (blur)="saveText(it)">
</div>
<div *ngIf="customTypeGroupArr[it.gIdx].anima" align="center" style="margin-top: 5px">
<button nz-button (click)="setAnimaBtnClick(i)" >
<i nz-icon nzType="tool" nzTheme="outline"></i>
配置龙骨动画
</button>
</div>
<div *ngIf="customTypeGroupArr[it.gIdx].animaSmall" align="center" style="margin-top: 5px">
<button nz-button (click)="setAnimaSmallBtnClick(i)" >
<i nz-icon nzType="tool" nzTheme="outline"></i>
配置龙骨动画(小)
</button>
</div>
<div *ngIf="customTypeGroupArr[it.gIdx].audio" style="display: flex;align-items: center; margin-top: 5px">
<app-audio-recorder
style="margin: auto"
[audioUrl]="it.audio_url"
(audioUploaded)="onItemAudioUploadSuccess($event, it)"
></app-audio-recorder>
</div>
<div *ngIf="customTypeGroupArr[it.gIdx]?.action" style="display: flex;align-items: center; margin-top: 5px">
<app-custom-action
style="margin: auto"
[item]="it ? it['actionData_' + it.gIdx] : {}"
[type]="customTypeGroupArr[it.gIdx].action.type"
[option]="customTypeGroupArr[it.gIdx].action.option"
(save)="onSaveCustomAction($event, it)">
></app-custom-action>
</div>
<div *ngIf="customTypeGroupArr[it.gIdx]?.isShowPos" style="display: flex; align-items: center; justify-content: center; margin-top: 5px;">
x: <input type="text" nz-input [(ngModel)]="it.posX" style="width: 50px; margin-right: 15px;" (blur)="saveItemPos(it)">
y: <input type="text" nz-input [(ngModel)]="it.posY" style="width: 50px" (blur)="saveItemPos(it)">
</div>
<div *ngIf="customTypeGroupArr[it.gIdx]?.select" align="center" >
<nz-select [(ngModel)]="it.selectType" nzAllowClear nzPlaceHolder="Choose" style="width: 70%; margin-top: 5px;">
<nz-option *ngFor="let s of customTypeGroupArr[it.gIdx]?.select" [nzValue]="s.value" [nzLabel]="s.label">
</nz-option>
</nz-select>
</div>
<div *ngIf="customTypeGroupArr[it.gIdx]?.label" align="center" style="margin-top: 5px; display: flex; align-items: center; justify-content: center;">
<span style="width: 30%;">{{customTypeGroupArr[it.gIdx].label + ':'}}</span>
<input type="text" nz-input [(ngModel)]="it.labelText" (blur)="saveText(it)">
</div>
<div *ngIf="customTypeGroupArr[it.gIdx]?.isCopy" align="center" style="margin-top: 5px; display: flex; align-items: center; justify-content: center;">
<button nz-button (click)="copyItem(it)" >
<i nz-icon nzType="copy" nzTheme="outline"></i>
复制粘贴
</button>
</div>
</div>
<!-- <div *ngIf="!customTypeGroupArr">
<div *ngIf="it.itemType == 'pic'">
<app-upload-image-with-preview
[picUrl]="it?.pic_url"
......@@ -52,13 +143,26 @@
<input type="text" nz-input [(ngModel)]="it.text" (blur)="saveText(it)">
</div>
<div style="width: 100%; margin-top: 5px;">
<div *ngIf="isHasAudio"
style="width: 100%; margin-top: 5px; display: flex; align-items: center; justify-items: center;">
<app-audio-recorder
style="margin: auto"
[audioUrl]="it.audio_url"
(audioUploaded)="onItemAudioUploadSuccess($event, it)"
></app-audio-recorder>
</div>
<div *ngIf="it.itemType == 'rect' && isHasAnima" align="center" style="margin-bottom: 5px">
<button nz-button (click)="setAnimaBtnClick(i)" >
<i nz-icon nzType="tool" nzTheme="outline"></i>
配置龙骨动画
</button>
</div>
</div> -->
</div>
</div>
......@@ -83,12 +187,29 @@
<div class="save-box">
<button class="save-btn" nz-button nzType="primary" [nzSize]="'large'" nzShape="round"
<button style="margin-left: 200px" class="save-btn" nz-button nzType="primary" [nzSize]="'large'" nzShape="round"
(click)="saveClick()" >
<i nz-icon nzType="save"></i>
Save
</button>
<div *ngIf="isCopyData" style="height: 40px; display: flex; justify-items: center;" >
<label style="margin-left: 40px" nz-checkbox [(ngModel)]="bgItem.isShowDebugLine">显示辅助框</label>
<button style="margin-left: 20px; margin-top: -5px" nz-button (click)="copyHotZoneData()"> 复制基础数据 </button>
<div style="margin-left: 10px; margin-top: -5px" >
<span>粘贴数据: </span>
<input style="width: 100px;" type="text" nz-input [(ngModel)]="pasteData" >
<button style="margin-left: 5px;" nz-button [disabled]="pasteData==''" nzType="primary" (click)="importData()">导入</button>
</div>
</div>
</div>
......@@ -98,3 +219,52 @@
<label style="opacity: 0; position: absolute; top: 0px; font-family: 'BRLNSR_1'">1</label>
<!--龙骨面板-->
<nz-modal [(nzVisible)]="animaPanelVisible" nzTitle="配置资源文件" (nzAfterClose)="closeAfterPanel()" (nzOnCancel)="animaPanelCancel()" (nzOnOk)="animaPanelOk()" nzOkText="保存">
<div class="anima-upload-btn">
<span style="margin-right: 10px">上传 ske_json 文件: </span>
<nz-upload [nzShowUploadList]="false"
nzAccept="application/json"
[nzAction]="uploadUrl"
[nzData]="uploadData"
(nzChange)="skeJsonHandleChange($event)">
<button nz-button><i nz-icon nzType="upload"></i><span>Upload</span></button>
</nz-upload>
<i *ngIf="isSkeJsonLoading" style="margin-left: 10px;" nz-icon [nzType]="'loading'"></i>
<span *ngIf="skeJsonData['name']" style="margin-left: 10px"><u> {{skeJsonData['name']}} </u></span>
</div>
<div class="anima-upload-btn">
<span style="margin-right: 10px">上传 tex_json 文件: </span>
<nz-upload [nzShowUploadList]="false"
nzAccept="application/json"
[nzAction]="uploadUrl"
[nzData]="uploadData"
(nzChange)="texJsonHandleChange($event)">
<button nz-button><i nz-icon nzType="upload"></i><span>Upload</span></button>
</nz-upload>
<i *ngIf="isTexJsonLoading" style="margin-left: 10px;" nz-icon [nzType]="'loading'"></i>
<span *ngIf="texJsonData['name']" style="margin-left: 10px"><u> {{texJsonData['name']}} </u></span>
</div>
<div class="anima-upload-btn">
<span style="margin-right: 10px">上传 tex_png 文件: </span>
<nz-upload [nzShowUploadList]="false"
nzAccept = "image/*"
[nzAction]="uploadUrl"
[nzData]="uploadData"
(nzChange)="texPngHandleChange($event)">
<button nz-button><i nz-icon nzType="upload"></i><span>Upload</span></button>
</nz-upload>
<i *ngIf="isTexPngLoading" style="margin-left: 10px;" nz-icon [nzType]="'loading'"></i>
<span *ngIf="texPngData['name']" style="margin-left: 10px"><u> {{texPngData['name']}} </u></span>
</div>
<div class="anima-upload-btn" *ngIf="customTypeGroupArr && customTypeGroupArr[curDragonBoneGIdx] && customTypeGroupArr[curDragonBoneGIdx].animaNames">
提示:需包含以下动画: {{customTypeGroupArr[curDragonBoneGIdx].animaNames.toString()}}
</div>
</nz-modal>
......@@ -81,6 +81,10 @@
}
}
.anima-upload-btn {
padding: 10px;
}
h5 {
margin-top: 1rem;
}
......@@ -89,8 +93,8 @@ h5 {
@font-face
{
font-family: 'BRLNSR_1';
src: url("/assets/font/BRLNSR_1.TTF") ;
font-family: 'ahronbd-1';
src: url("/assets/font/ahronbd-1.ttf") ;
}
......@@ -105,106 +109,3 @@ h5 {
//@import '../../../style/common_mixin';
//
//.p-image-uploader {
// position: relative;
// display: block;
// width: 100%;
// height: 0;
// padding-bottom: 56.25%;
// .p-box {
// position: absolute;
// left: 0;
// top: 0;
// right: 0;
// bottom: 0;
// border: 2px dashed #ddd;
// border-radius: 0.5rem;
// background-color: #fafafa;
// text-align: center;
// color: #aaa;
// .p-upload-icon {
// text-align: center;
// margin: auto;
// .anticon-upload {
// color: #888;
// font-size: 5rem;
// }
// .p-progress-bar {
// position: relative;
// width: 20rem;
// height: 1.5rem;
// border: 1px solid #ccc;
// border-radius: 1rem;
// .p-progress-bg {
// background-color: #1890ff;
// border-radius: 1rem;
// height: 100%;
// }
// .p-progress-value {
// position: absolute;
// top: 0;
// left: 0;
// width: 100%;
// height: 100%;
// text-shadow: 0 0 4px #000;
// color: white;
// text-align: center;
// font-size: 0.9rem;
// line-height: 1.5rem;
// }
// }
// }
// .p-preview {
// width: 100%;
// height: 100%;
// //background-image: url("https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png");
// @include k-img-bg();
// }
// }
//}
//
//.p-btn-delete {
// position: absolute;
// right: -0.5rem;
// top: -0.5rem;
// width: 2rem;
// height: 2rem;
// border: 0.2rem solid white;
// border-radius: 50%;
// font-size: 1.2rem;
// background-color: #fb781a;
// color: white;
// text-align: center;
//}
//
//.p-upload-progress-bg {
// position: absolute;
// width: 100%;
// height: 100%;
// display: flex;
// align-items: center;
// justify-content: center;
// & .i-text {
// position: absolute;
// text-align: center;
// color: white;
// text-shadow: 0 0 2px rgba(0, 0, 0, .85);
// }
// & .i-bg {
// position: absolute;
// left: 0;
// top: 0;
// height: 100%;
// background-color: #27b43f;
// border-radius: 0.5rem;
// }
//}
//
//
//:host ::ng-deep .ant-upload {
// display: block;
//}
import {
ApplicationRef,
ChangeDetectorRef,
Component,
ElementRef,
EventEmitter,
......@@ -12,10 +14,11 @@ import {
} from '@angular/core';
import {Subject} from 'rxjs';
import {debounceTime} from 'rxjs/operators';
import {EditorItem, HotZoneImg, HotZoneItem, HotZoneLabel, Label, MySprite, removeItemFromArr} from './Unit';
import {DragItem, EditorItem, HotZoneImg, HotZoneItem, HotZoneLabel, Label, MySprite, removeItemFromArr, ShapeRect, ShapeRectNew} from './Unit';
import TWEEN from '@tweenjs/tween.js';
import {getMinScale} from "../../play/Unit";
import {tar} from "compressing";
import {getMinScale} from '../../play/Unit';
import {tar} from 'compressing';
import {NzMessageService} from 'ng-zorro-antd';
@Component({
......@@ -25,18 +28,14 @@ import {tar} from "compressing";
})
export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
@ViewChild('canvas', {static: true}) canvas: ElementRef;
@ViewChild('wrap', {static: true}) wrap: ElementRef;
@Input()
imgItemArr = null;
@Input()
hotZoneItemArr = null;
@Input()
hotZoneArr = null;
@Output()
save = new EventEmitter();
@ViewChild('canvas', {static: true}) canvas: ElementRef;
@ViewChild('wrap', {static: true}) wrap: ElementRef;
@Input()
isHasRect = true;
@Input()
......@@ -44,16 +43,31 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
@Input()
isHasText = true;
@Input()
hotZoneFontObj = {
size: 50,
name: 'BRLNSR_1',
color: '#8f3758'
}
isHasAudio = true;
@Input()
defaultItemType = 'text';
isHasAnima = false;
@Input()
customTypeGroupArr; // [{name:string, rect:boolean, pic:boolean, text:boolean, audio:boolean, anima:boolean, animaNames:['name1', ..]}, ...]
@Input()
// hotZoneFontObj;
@Input()
isCopyData = false;
hotZoneFontObj;
// hotZoneFontObj = {
// size: 50,
// name: 'ahronbd-1',
// color: '#8f3758'
// }
@Input()
hotZoneImgSize = 190;
defaultItemType = 'text';
@Input()
hotZoneImgSize = 0;
@Output()
save = new EventEmitter();
saveDisabled = true;
......@@ -86,8 +100,42 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
changeSizeFlag = false;
changeTopSizeFlag = false;
changeRightSizeFlag = false;
constructor() {
animaPanelVisible = false;
uploadUrl;
uploadData;
skeJsonData = {};
texJsonData = {};
texPngData = {};
animaName = '';
curDragonBoneIndex;
curDragonBoneGIdx;
pasteData = '';
isSkeJsonLoading = false;
isTexJsonLoading = false;
isTexPngLoading = false;
isAnimaSmall = false;
savePropertyArr = [
'id',
'gIdx',
'selectType',
'labelText',
'posX',
'posY'
]
constructor(private nzMessageService: NzMessageService, private appRef: ApplicationRef, private changeDetectorRef: ChangeDetectorRef) {
this.uploadUrl = (<any> window).courseware.uploadUrl();
this.uploadData = (<any> window).courseware.uploadData();
window['air'].getUploadCallback = (url, data) => {
this.uploadUrl = url;
this.uploadData = data;
};
}
_bgItem = null;
......@@ -110,11 +158,14 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
ngOnInit() {
this.initListener();
// this.init();
this.update();
this.refresh();
}
ngOnDestroy() {
......@@ -136,10 +187,12 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
onItemImgUploadSuccess(e, item) {
item.pic_url = e.url;
this.loadHotZonePic(item.pic, e.url);
this.refresh();
}
onItemAudioUploadSuccess(e, item) {
item.audio_url = e.url;
this.refresh();
}
......@@ -150,8 +203,12 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
this.renderArr.push(this.bg);
}
if (!this.bgItem.url) {
this.bgItem.url = 'http://teach.cdn.ireadabc.com/8ebb1858564340ea0936b83e3280ad7d.jpg';
}
const bg = this.bg;
if (this.bgItem.url) {
// if (this.bgItem.url) {
bg.load(this.bgItem.url).then(() => {
const rate1 = this.canvasWidth / bg.width;
......@@ -160,27 +217,47 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
const rate = Math.min(rate1, rate2);
bg.setScaleXY(rate);
bg.x = this.canvasWidth / 2;
bg.y = this.canvasHeight / 2;
bg.removeChildren();
const bgBorder = new ShapeRectNew(this.ctx);
bgBorder.setSize(bg.width, bg.height, 0);
bgBorder.fillColor = '#ff0000';
bgBorder.fill = false;
bgBorder.stroke = true;
bgBorder.x = -bg.width / 2;
bgBorder.y = -bg.height / 2;
bgBorder.lineWidth = 0.5;
bg.addChild(bgBorder);
if (callBack) {
callBack();
}
this.refresh();
});
}
// }
}
addBtnClick() {
addBtnClick(data=null) {
// this.imgArr.push({});
// this.hotZoneArr.push({});
const item = this.getHotZoneItem();
const item = this.getHotZoneItem(data);
this.hotZoneArr.push(item);
if (this.customTypeGroupArr) {
this.refreshCustomItem(item);
} else {
this.refreshItem(item);
}
this.refreshHotZoneId();
......@@ -191,6 +268,7 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
const item = this.hotZoneArr.splice(index, 1)[0];
removeItemFromArr(this.renderArr, item.pic);
removeItemFromArr(this.renderArr, item.textLabel);
removeItemFromArr(this.renderArr, item.drag);
this.refreshHotZoneId();
......@@ -199,6 +277,7 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
onImgUploadSuccessByImg(e, img) {
img.pic_url = e.url;
this.refreshImage(img);
this.refresh();
}
refreshImage(img) {
......@@ -221,6 +300,7 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
}
}
this.refresh();
}
......@@ -245,10 +325,18 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
item.anchorX = 0.5;
item.anchorY = 0.5;
item.x = this.canvasWidth / 2;
item.y = this.canvasHeight / 2;
item.itemType = this.defaultItemType;
item.itemType = this.getDefaultItemType();
item.gIdx = '0';
item['id'] = this.createItemId() // new Date().getTime().toString();
item['data'] = saveData;
console.log('item.x: ', item.x);
if (saveData) {
......@@ -257,41 +345,203 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
item.scaleY = saveRect.height / item.height;
item.x = saveRect.x + saveRect.width / 2;
item.y = saveRect.y + saveRect.height / 2;
item.gIdx = saveData.gIdx;
item['id'] = saveData.id;
item['skeJsonData'] = saveData.skeJsonData;
item['texJsonData'] = saveData.texJsonData;
item['texPngData'] = saveData.texPngData;
item['actionData_' + item.gIdx] = saveData['actionData_' + item.gIdx];
this.savePropertyArr.forEach((key) => {
if (saveData[key]) {
item[key] = saveData[key];
}
})
}
console.log('item.x:~~ ', item.x);
item.showLineDash();
const pic = new HotZoneImg(this.ctx);
// const pic = new HotZoneImg(this.ctx);
this.setItemPic(item, saveData);
this.setItemLabel(item, saveData);
this.setItemAnima(item, saveData);
this.setItemDrag(item, saveData);
return item;
}
setItemPic(item, saveData) {
console.log('in setItemPic ', saveData);
const pic = new EditorItem(this.ctx);
pic.visible = false;
item['pic'] = pic;
if (saveData && saveData.pic_url) {
this.loadHotZonePic(pic, saveData.pic_url);
if (saveData) {
let picUrl = saveData.pic_url;
const actionData = saveData['actionData_' + item.gIdx];
if (actionData && actionData.type == 'pic') {
picUrl = actionData.pic_url;
}
console.log('saveData: ', saveData);
console.log('picUrl: ', picUrl);
if (picUrl) {
this.loadHotZonePic(pic, picUrl, saveData.imgScale);
}
}
pic.x = item.x;
pic.y = item.y;
this.renderArr.push(pic);
}
setItemDrag(item, saveData) {
console.log('in setItemDrag ', saveData);
const dragItem = new DragItem(this.ctx);
dragItem.init();
dragItem.item = item;
item['drag'] = dragItem;
dragItem.visible = false;
dragItem.x = item.x;
dragItem.y = item.y;
this.renderArr.push(dragItem);
if (saveData) {
if (saveData.dragDot) {
dragItem.x = saveData.dragDot.x / saveData.mapScale * this.mapScale;
dragItem.y = saveData.dragDot.y / saveData.mapScale * this.mapScale;
}
}
// console.log('item.itemType: ', item.itemType);
// let w = item.width;
// let h = item.height;
// if (saveData) {
// switch (saveData.itemType) {
// case 'rect':
// w = saveData.rect.width;
// h = saveData.rect.height;
// break;
// case 'pic':
// w = saveData.imgSizeW * saveData.imgScale;
// h = saveData.imgSizeH * saveData.imgScale;;
// break;
// case 'text':
// w = saveData.rect.width;
// h = saveData.rect.height;
// break;
// }
// }
// dragItem.setSize(w, h);
}
setItemAnima(item, saveData) {
console.log('in setItemAnima ', saveData);
// const pic = new EditorItem(this.ctx);
// pic.visible = false;
// item['pic'] = pic;
// if (saveData) {
// let picUrl = saveData.pic_url;
// const actionData = saveData['actionData_' + item.gIdx];
// if (actionData && actionData.type == 'pic') {
// picUrl = actionData.pic_url;
// }
// console.log('saveData: ', saveData);
// console.log('picUrl: ', picUrl);
// if (picUrl) {
// this.loadHotZonePic(pic, picUrl, saveData.imgScale);
// }
// }
// pic.x = item.x;
// pic.y = item.y;
// this.renderArr.push(pic);
}
setItemLabel(item, saveData) {
const textLabel = new HotZoneLabel(this.ctx);
if (this.hotZoneFontObj) {
textLabel.fontSize = this.hotZoneFontObj.size;
textLabel.fontName = this.hotZoneFontObj.name;
textLabel.fontColor = this.hotZoneFontObj.color;
}
textLabel.textAlign = 'center';
// textLabel.setOutline();
// console.log('saveData:', saveData);
item['textLabel'] = textLabel;
textLabel.setScaleXY(this.mapScale);
if (saveData && saveData.text) {
textLabel.text = saveData.text;
if (saveData) {
if (saveData.text && this.hotZoneFontObj) {
textLabel.text = saveData.text
}
this.setActionFont(textLabel, saveData['actionData_' + item.gIdx]);
textLabel.refreshSize();
}
textLabel.x = item.x;
textLabel.y = item.y;
this.renderArr.push(textLabel);
}
return item;
setActionFont(textLabel, actionData) {
if (actionData && actionData.type == 'text') {
textLabel.text = actionData.text;
for (let i=0; i<actionData.changeOption.length; i++) {
const op = actionData.changeOption[i];
textLabel[op[0]] = op[1];
}
}
}
getDefaultItemType() {
if (this.customTypeGroupArr) {
const group = this.customTypeGroupArr[0];
if (group.rect) {
return 'rect';
}
if (group.pic) {
return 'pic';
}
if (group.text) {
return 'text';
}
} else {
return this.defaultItemType;
}
}
getPicItem(img, saveData = null) {
......@@ -327,9 +577,11 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
item.y = saveRect.y + saveRect.height / 2;
} else {
item.showLineDash();
// item.showLineDash();
}
item.showLineDash();
console.log('aaa');
});
return item;
}
......@@ -337,6 +589,7 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
onAudioUploadSuccessByImg(e, img) {
img.audio_url = e.url;
this.refresh();
}
deleteItem(e, i) {
......@@ -345,15 +598,111 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
this.hotZoneArr.splice(i, 1);
this.refreshHotZoneId();
this.refresh();
}
radioChange(e, item) {
item.itemType = e;
this.refreshItem(item);
// radioChange(e, item) {
// item.itemType = e;
// this.refreshItem(item);
// this.refresh();
// // console.log(' in radioChange e: ', e);
// }
customRadioChange(e, item) {
console.log('in customRadioChange', e);
item.gIdx = -1;
setTimeout(() => {
item.gIdx = e;
this.refreshCustomItem(item);
this.refresh();
}, 1);
// const curGroup = this.customTypeGroupArr[e];
}
refreshCustomItem(item) {
this.hideHotZoneItem(item);
const group = this.customTypeGroupArr[item.gIdx];
if (!group) {
return;
}
if (group.text) {
this.showItemLabel(item);
}
if (group.rect) {
this.showItemRect(item, !group.isFixed);
}
if (group.pic && !group.anima) {
this.showItemPic(item);
}
if (group.action) {
if (group.action.type == 'pic') {
this.showItemPic(item);
}
if (group.action.type == 'text') {
this.showItemLabel(item);
}
if (group.action.type == 'anima') {
this.showItemRect(item);
}
}
item.drag.visible = group.drag;
item.setAnimaStyle(group.animaSmall)
}
// console.log(' in radioChange e: ', e);
showItemDrag(item) {
item.drag.visible = true;
// item.dragBody.visible = true;
// item.itemType = 'pic';
// this.showArrowTop(item)
}
showItemPic(item) {
item.pic.visible = true;
item.itemType = 'pic';
this.showArrowTop(item)
}
showItemLabel(item) {
item.textLabel.visible = true;
item.itemType = 'text';
this.showArrowTop(item)
}
showItemRect(item, isShowArrowTop = true) {
item.visible = true;
item.itemType = 'rect';
this.showArrowTop(item, isShowArrowTop)
}
showArrowTop(item, isShowArrowTop = false) {
if (isShowArrowTop) {
item.arrowTop.visible = true;
item.arrowRight.visible = true;
} else {
item.arrowTop.visible = false;
item.arrowRight.visible = false;
}
}
hideHotZoneItem(item) {
item.visible = false;
item.pic.visible = false;
item.textLabel.visible = false;
}
refreshItem(item) {
......@@ -368,32 +717,29 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
this.setTextState(item);
break;
default:
this.setNoneState(item);
}
}
init() {
console.log('init');
this.initData();
this.initCtx();
this.initItem();
}
initItem() {
this.changeDetectorRef.markForCheck();
this.changeDetectorRef.detectChanges();
if (!this.bgItem) {
this.bgItem = {};
} else {
this.refreshBackground(() => {
// if (!this.imgItemArr) {
// this.imgItemArr = [];
// } else {
// this.initImgArr();
// }
// console.log('aaaaa');
if (!this.hotZoneItemArr) {
this.hotZoneItemArr = [];
......@@ -404,6 +750,7 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
});
}
this.refresh();
}
initHotZoneArr() {
......@@ -427,6 +774,7 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
this.hotZoneArr = [];
const arr = this.hotZoneItemArr.concat();
console.log('this.hotZoneItemArr: ', this.hotZoneItemArr);
for (let i = 0; i < arr.length; i++) {
const data = JSON.parse(JSON.stringify(arr[i]));
......@@ -449,9 +797,15 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
item.pic_url = data.pic_url;
item.text = data.text;
item.itemType = data.itemType;
if (this.customTypeGroupArr) {
this.refreshCustomItem(item);
} else {
this.refreshItem(item);
}
console.log('item: ', item);
console.log('1 item: ', item);
this.hotZoneArr.push(item);
}
......@@ -463,48 +817,6 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
}
initImgArr() {
console.log('this.imgItemArr: ', this.imgItemArr);
let curBgRect;
if (this.bg) {
curBgRect = this.bg.getBoundingBox();
} else {
curBgRect = {x: 0, y: 0, width: this.canvasWidth, height: this.canvasHeight};
}
let oldBgRect = this.bgItem.rect;
if (!oldBgRect) {
oldBgRect = curBgRect;
}
const rate = curBgRect.width / oldBgRect.width;
console.log('rate: ', rate);
this.imgArr = [];
const arr = this.imgItemArr.concat();
for (let i = 0; i < arr.length; i++) {
const data = JSON.parse(JSON.stringify(arr[i]));
const img = {pic_url: data.pic_url};
data.rect.x *= rate;
data.rect.y *= rate;
data.rect.width *= rate;
data.rect.height *= rate;
data.rect.x += curBgRect.x;
data.rect.y += curBgRect.y;
img['picItem'] = this.getPicItem(img, data);
img['audio_url'] = arr[i].audio_url;
this.imgArr.push(img);
}
this.refreshImageId();
}
initData() {
this.canvasWidth = this.wrap.nativeElement.clientWidth;
......@@ -528,9 +840,28 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
this.oldPos = {x: this.mx, y: this.my};
for (let i = 0; i < this.hotZoneArr.length; i++) {
// 先检测拖拽点
for (let i = this.hotZoneArr.length - 1; i >= 0; i--) {
const item = this.hotZoneArr[i];
if (item && item.drag && item.drag.visible) {
if (this.checkClickTarget(item.drag)) {
this.clickedDragPoint(item.drag);
return;
}
}
}
for (let i = this.hotZoneArr.length - 1; i >= 0; i--) {
const item = this.hotZoneArr[i];
console.log('mapDown item: ', item);
let callback;
let target;
switch (item.itemType) {
......@@ -548,7 +879,7 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
break;
}
if (this.checkClickTarget(target)) {
if (target && this.checkClickTarget(target)) {
callback(target);
return;
}
......@@ -578,15 +909,18 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
const addY = this.my - this.oldPos.y;
this.curItem.x += addX;
this.curItem.y += addY;
this.curItem.posX = this.curItem.x;
this.curItem.posY = this.curItem.y;
}
this.oldPos = {x: this.mx, y: this.my};
this.saveDisabled = true;
// this.saveDisabled = false;
}
mapUp(event) {
mapUp(event=null) {
this.curItem = null;
this.changeSizeFlag = false;
this.changeTopSizeFlag = false;
......@@ -685,10 +1019,13 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
changeCurItem(item) {
console.log(' in changeCurItem', item)
this.hideAllLineDash();
this.curItem = item;
this.curItem.showLineDash();
}
hideAllLineDash() {
......@@ -711,9 +1048,7 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
// 清除画布内容
this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
for (let i = 0; i < this.renderArr.length; i++) {
this.renderArr[i].update(this);
}
// for (let i = 0; i < this.imgArr.length; i++) {
// const picItem = this.imgArr[i].picItem;
......@@ -722,8 +1057,16 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
// }
// }
for (let i = 0; i < this.renderArr.length; i++) {
this.renderArr[i].update(this);
}
this.updateArr(this.hotZoneArr);
this.updatePos()
this.updatePos();
TWEEN.update();
......@@ -833,7 +1176,9 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
checkClickTarget(target) {
if (!target || !target.visible) {
return;
}
const rect = target.getBoundingBox();
if (this.checkPointInRect(this.mx, this.my, rect)) {
return true;
......@@ -852,9 +1197,21 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
saveClick() {
const sendData = this.getSendData();
this.save.emit(sendData);
}
getSendData() {
const bgItem = this.bgItem;
if (this.bg) {
bgItem['rect'] = this.bg.getBoundingBox();
const rect = this.bg.getBoundingBox();
bgItem['rect'] = rect;
rect.x = Math.round(rect.x * 100) / 100;
rect.y = Math.round(rect.y * 100) / 100;
rect.width = Math.round(rect.width * 100) / 100;
rect.height = Math.round(rect.height * 100) / 100;
} else {
bgItem['rect'] = {
x: 0,
......@@ -870,19 +1227,52 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
for (let i = 0; i < hotZoneArr.length; i++) {
const hotZoneItem = {
id: hotZoneArr[i].id,
index: hotZoneArr[i].index,
pic_url: hotZoneArr[i].pic_url,
text: hotZoneArr[i].text,
audio_url: hotZoneArr[i].audio_url,
itemType: hotZoneArr[i].itemType,
fontSize: this.hotZoneFontObj.size,
fontName: this.hotZoneFontObj.name,
fontColor: this.hotZoneFontObj.color,
fontScale: hotZoneArr[i].textLabel ? hotZoneArr[i].textLabel.scaleX : 1,
imgScale: hotZoneArr[i].pic ? hotZoneArr[i].pic.scaleX : 1,
mapScale: this.mapScale
imgSizeW: hotZoneArr[i].pic ? hotZoneArr[i].pic.width : 0,
imgSizeH: hotZoneArr[i].pic ? hotZoneArr[i].pic.height : 0,
mapScale: this.mapScale,
skeJsonData: hotZoneArr[i].skeJsonData,
texJsonData: hotZoneArr[i].texJsonData,
texPngData: hotZoneArr[i].texPngData,
dragDot: hotZoneArr[i].drag.getPosition(),
gIdx: hotZoneArr[i].gIdx,
};
this.savePropertyArr.forEach((key) => {
if (hotZoneArr[i][key]) {
hotZoneItem[key] = hotZoneArr[i][key];
}
})
hotZoneItem['actionData_' + hotZoneItem.gIdx] = hotZoneArr[i]['actionData_' + hotZoneArr[i].gIdx]
if (this.hotZoneFontObj) {
hotZoneItem['fontSize'] = this.hotZoneFontObj.size;
hotZoneItem['fontName'] = this.hotZoneFontObj.name;
hotZoneItem['ontColor'] = this.hotZoneFontObj.color;
}
if (hotZoneArr[i].itemType == 'pic') {
hotZoneItem['rect'] = hotZoneArr[i].pic.getBoundingBox();
} else if (hotZoneArr[i].itemType == 'text') {
hotZoneArr[i].textLabel.refreshSize();
hotZoneItem['rect'] = hotZoneArr[i].textLabel.getLabelRect();
// hotZoneItem['rect'].width = hotZoneItem['rect'].width / hotZoneArr[i].textLabel.scaleX;
// hotZoneItem['rect'].height = hotZoneArr[i].textLabel.height * hotZoneArr[i].textLabel.scaleY;
} else {
hotZoneItem['rect'] = hotZoneArr[i].getBoundingBox();
}
// hotZoneItem['rect'] = hotZoneArr[i].getBoundingBox();
hotZoneItem['rect'].x = Math.round((hotZoneItem['rect'].x - bgItem['rect'].x) * 100) / 100;
hotZoneItem['rect'].y = Math.round((hotZoneItem['rect'].y - bgItem['rect'].y) * 100) / 100;
hotZoneItem['rect'].width = Math.round((hotZoneItem['rect'].width) * 100) / 100;
......@@ -894,7 +1284,360 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
console.log('hotZoneItemArr: ', hotZoneItemArr);
this.save.emit({bgItem, hotZoneItemArr});
return {bgItem, hotZoneItemArr};
}
saveText(item) {
if (item.itemType == 'text') {
item.textLabel.text = item.text;
}
}
saveItemPos(item) {
console.log('item.posX: ', item.posX)
console.log('item.posY: ', item.posY)
item.x = Number(item.posX || 0)
item.y = Number(item.posY || 0)
// this.changeCurItem(item);
// this.curItem.x = item.posX || 0;
// this.curItem.y = item.posY || 0;
// this.mapUp();
}
onSaveCustomAction(e, item) {
const data = e;
item['actionData_' + item.gIdx] = data;
if (data.type == 'pic') {
let picUrl = data.pic_url;
if (picUrl) {
this.loadHotZonePic(item.pic, picUrl);
}
}
if (data.type == 'text') {
this.setActionFont(item.textLabel, data);
item.textLabel.refreshSize();
}
// if (data.type == 'anima') {
// this.setActionAnima(item.anima, data);
// }
// this.loadHotZonePic(item.pic, e.url);
this.refresh()
}
setActionAnima() {
}
setAnimaBtnClick(index) {
console.log('aaaa')
this.isAnimaSmall = false;
this.setCurDragonBone(index);
// this.curDragonBoneIndex = index;
// this.curDragonBoneGIdx = Number(this.hotZoneArr[index].gIdx);
// const {skeJsonData, texJsonData, texPngData} = this.hotZoneArr[index];
// this.skeJsonData = skeJsonData || {};
// this.texJsonData = texJsonData || {};
// this.texPngData = texPngData || {};
// this.animaPanelVisible = true;
// this.refresh();
}
setAnimaSmallBtnClick(index) {
console.log('bbb')
this.isAnimaSmall = true;
this.setCurDragonBone(index);
}
setCurDragonBone(index) {
this.curDragonBoneIndex = index;
this.curDragonBoneGIdx = Number(this.hotZoneArr[index].gIdx);
const {skeJsonData, texJsonData, texPngData} = this.hotZoneArr[index];
this.skeJsonData = skeJsonData || {};
this.texJsonData = texJsonData || {};
this.texPngData = texPngData || {};
this.animaPanelVisible = true;
this.refresh();
}
setItemSizeByAnima(jsonData, item) {
console.log('json: ', jsonData);
const request = new XMLHttpRequest();
//通过url获取文件,第二个选项是文件所在的具体地址
request.open('GET', jsonData.url, true);
request.send(null);
request.onreadystatechange = ()=> {
if (request.readyState === 4 && request.status === 200) {
var type = request.getResponseHeader('Content-Type');
if (type.indexOf("text") !== 1) {
//返回一个文件内容的对象
const data = JSON.parse(request.responseText);
console.log('request.responseText;', data)
const animaSize = data.armature[0].canvas;
item.width = animaSize.width;
item.height = animaSize.height;
// return request.responseText;
}
}
}
}
animaPanelCancel() {
this.animaPanelVisible = false;
this.refresh();
}
animaPanelOk() {
this.setItemDragonBoneData(this.hotZoneArr[this.curDragonBoneIndex]);
console.log('this.hotZoneArr: ', this.hotZoneArr);
this.animaPanelVisible = false;
if (this.isAnimaSmall) {
this.setItemSizeByAnima(this.skeJsonData, this.hotZoneArr[this.curDragonBoneIndex]);
}
this.refresh();
}
setItemDragonBoneData(item) {
item['skeJsonData'] = this.skeJsonData;
item['texJsonData'] = this.texJsonData;
item['texPngData'] = this.texPngData;
}
skeJsonHandleChange(e) {
console.log('e: ', e);
switch (e.type) {
case 'start':
this.isSkeJsonLoading = true;
break;
case 'success':
this.skeJsonData['url'] = e.file.response.url;
this.skeJsonData['name'] = e.file.name;
this.nzMessageService.success('上传成功');
this.isSkeJsonLoading = false;
break;
case 'progress':
break;
}
}
texJsonHandleChange(e) {
console.log('e: ', e);
switch (e.type) {
case 'start':
this.isTexJsonLoading = true;
break;
case 'success':
this.texJsonData['url'] = e.file.response.url;
this.texJsonData['name'] = e.file.name;
this.nzMessageService.success('上传成功');
this.isTexJsonLoading = false;
break;
case 'progress':
break;
}
}
texPngHandleChange(e) {
console.log('e: ', e);
switch (e.type) {
case 'start':
this.isTexPngLoading = true;
break;
case 'success':
this.texPngData['url'] = e.file.response.url;
this.texPngData['name'] = e.file.name;
this.nzMessageService.success('上传成功');
this.isTexPngLoading = false;
break;
case 'progress':
break;
}
}
copyItem(it) {
const {hotZoneItemArr} = this.getSendData();
let itemData;
hotZoneItemArr.forEach((data) => {
if (data.id == it.id) {
itemData = data;
}
})
let curBgRect;
if (this.bg) {
curBgRect = this.bg.getBoundingBox();
} else {
curBgRect = {x: 0, y: 0, width: this.canvasWidth, height: this.canvasHeight};
}
let oldBgRect = this.bgItem.rect;
if (!oldBgRect) {
oldBgRect = curBgRect;
}
const rate = curBgRect.width / oldBgRect.width;
console.log('rate: ', rate);
const data = itemData
data.rect.x *= rate;
data.rect.y *= rate;
data.rect.width *= rate;
data.rect.height *= rate;
data.rect.x += curBgRect.x;
data.rect.y += curBgRect.y;
const item = this.getHotZoneItem(data);
item.audio_url = data.audio_url;
item.pic_url = data.pic_url;
item.text = data.text;
item.itemType = data.itemType;
this.hotZoneArr.push(item);
if (this.customTypeGroupArr) {
this.refreshCustomItem(item);
} else {
this.refreshItem(item);
}
this.refreshHotZoneId();
item['id'] = this.createItemId();
}
createItemId() {
return new Date().getTime().toString();
}
copyHotZoneData() {
const {bgItem, hotZoneItemArr} = this.getSendData();
// const hotZoneItemArrNew = [];
// const tmpArr = JSON.parse(JSON.stringify(hotZoneItemArr));
// tmpArr.forEach((item) => {
// if (item.gIdx === '0') {
// hotZoneItemArr.push(item);
// }
// })
const jsonData = {
bgItem,
hotZoneItemArr,
isHasRect: this.isHasRect,
isHasPic: this.isHasPic,
isHasText: this.isHasText,
isHasAudio: this.isHasAudio,
isHasAnima: this.isHasAnima,
hotZoneFontObj: this.hotZoneFontObj,
defaultItemType: this.defaultItemType,
hotZoneImgSize: this.hotZoneImgSize,
};
const oInput = document.createElement('input');
oInput.value = JSON.stringify(jsonData);
document.body.appendChild(oInput);
oInput.select(); // 选择对象
document.execCommand('Copy'); // 执行浏览器复制命令
document.body.removeChild(oInput);
this.nzMessageService.success('复制成功');
// alert('复制成功');
}
importData() {
if (!this.pasteData) {
return;
}
try {
const data = JSON.parse(this.pasteData);
console.log('data:', data);
const {
bgItem,
hotZoneItemArr,
isHasRect,
isHasPic,
isHasText,
isHasAudio,
isHasAnima,
hotZoneFontObj,
defaultItemType,
hotZoneImgSize,
} = data;
this.hotZoneItemArr = hotZoneItemArr;
this.isHasRect = isHasRect;
this.isHasPic = isHasPic;
this.isHasText = isHasText;
this.isHasAudio = isHasAudio;
this.isHasAnima = isHasAnima;
this.hotZoneFontObj = hotZoneFontObj;
this.defaultItemType = defaultItemType;
this.hotZoneImgSize = hotZoneImgSize;
this.bgItem = bgItem;
this.pasteData = '';
} catch (e) {
console.log('err: ', e);
this.nzMessageService.error('导入失败');
}
}
private updatePos() {
......@@ -917,12 +1660,15 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
break;
}
if (x != undefined && y != undefined) {
item.x = x;
item.y = y;
item.pic.x = x;
item.pic.y = y;
item.textLabel.x = x;
item.textLabel.y = y;
}
});
}
......@@ -944,6 +1690,12 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
item.textLabel.visible = true;
}
private setNoneState(item: any) {
item.visible = false;
item.pic.visible = false;
item.textLabel.visible = false;
}
private clickedHotZoneRect(item: any) {
if (this.checkClickTarget(item)) {
......@@ -962,6 +1714,17 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
private clickedHotZonePic(item: any) {
if (this.checkClickTarget(item)) {
if (item.lineDashFlag && this.checkClickTarget(item.arrow)) {
this.changeItemSize(item);
// } else if (item.lineDashFlag && this.checkClickTarget(item.arrowTop)) {
// this.changeItemTopSize(item);
// } else if (item.lineDashFlag && this.checkClickTarget(item.arrowRight)) {
// this.changeItemRightSize(item);
} else {
this.changeCurItem(item);
}
this.curItem = item;
}
}
......@@ -972,15 +1735,42 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
}
}
saveText(item) {
item.textLabel.text = item.text;
clickedDragPoint(item) {
this.curItem = item;
}
private loadHotZonePic(pic: HotZoneImg, url, scale = null) {
let baseLen;
if (this.hotZoneImgSize) {
baseLen = this.hotZoneImgSize * this.mapScale;
}
private loadHotZonePic(pic: HotZoneImg, url) {
const baseLen = this.hotZoneImgSize * this.mapScale;
pic.load(url).then(() => {
const s = getMinScale(pic, baseLen);
pic.setScaleXY(s);
if (!scale) {
if (baseLen) {
scale = getMinScale(pic, baseLen);
} else {
scale = this.bg.scaleX;
}
}
pic.setScaleXY(scale);
});
}
closeAfterPanel() {
this.refresh();
}
/**
* 刷新 渲染页面
*/
refresh() {
setTimeout(() => {
this.appRef.tick();
}, 1);
}
}
<div class="content">
<div style="display: flex; margin-bottom: 5px;">
<button nz-button nzType="dashed" (click)="selectSubTemplate()" style=" border-radius: 0.5rem; border: 1px solid #ddd;">
<i nz-icon nzType="appstore" nzTheme="outline"></i>
选择子模板类型
</button>
<h2 *ngIf="_item.template" style="margin-left: 10px; margin-top: 1px;">
{{_item.template.description}}
</h2>
<button nz-button nzType="danger" (click)="deleteSubTemplate()" style="position: absolute; right: 30px; border-radius: 0.5rem; border: 1px solid #ddd;">
<i nz-icon nzType="delete" nzTheme="outline"></i>
删除
</button>
</div>
<iframe #iframe id="iframe" [src]="safeUrl" style="width: 98%; height: 70vh; border: 1px solid #ccc; padding: 5px;" frameborder="0">
</iframe>
<nz-modal [(nzVisible)]="isShowTemplate" (nzAfterClose)="closePanel()" nzTitle="选择子模板" [nzFooter]="null"
(nzOnCancel)="panelCancelClicked()" nzWidth="80%" nzHeight="80%">
<div class="template-container">
<div *ngFor="let template of templateArr" >
<div (click)="templateClick(template)" style="width: 150px; height: 100px; border: 1px solid #ccc; margin: 5px; cursor:pointer; user-select: none;">
<div style="width: 100%; height: 70%; border-bottom: 1px solid #ccc; overflow: hidden;">
<img [src]="template.cover" style="width: 100%; height: auto;">
</div>
<div style="width: 100%; height: 30%; display: flex; align-items: center; justify-content: center; ">
<label style="width: 90%; text-align: center; text-overflow: ellipsis; white-space:nowrap; overflow: hidden; ">{{template.description}}</label>
</div>
</div>
</div>
</div>
</nz-modal>
</div>
\ No newline at end of file
@import '../../style/common_mixin.css';
.content {
// width: 100%;
height: 100%;
border: 2px solid #ccc;
padding: 10px;
border-radius: 5px;
}
.template-container{
width: 100%;
display: flex;
// align-items: center;
// justify-content: space-around;
flex-wrap: wrap;
}
\ No newline at end of file
import {ApplicationRef, Component, ElementRef, EventEmitter, Input, OnChanges, OnDestroy, Output, ViewChild} from '@angular/core';
import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';
import { NzMessageService, UploadXHRArgs, UploadFile } from 'ng-zorro-antd';
@Component({
selector: 'app-sub-template',
templateUrl: './sub-template.component.html',
styleUrls: ['./sub-template.component.scss']
})
export class SubTemplateComponent implements OnDestroy, OnChanges {
uploading = false;
progress = 0;
@Output()
save = new EventEmitter();
@Output()
deleteFunc = new EventEmitter();
@Output()
refreshEmitter = new EventEmitter();
@Input()
set item(v) {
console.log('set item: ', v);
if (this.uploadUrl) {
this._item = v;
console.log(' __ refreshSafeUrl 3')
this.refreshSafeUrl();
}
this._data = v;
}
get item() {
return this._data;
}
_item;
_data;
@Input()
key = '';
uploadUrl;
uploadData;
@ViewChild('iframe', {static: true }) iframe: ElementRef;
isShowTemplate = false;
constructor(private el:ElementRef, private appRef: ApplicationRef, private nzMessageService: NzMessageService, private sanitizer: DomSanitizer) {
console.log(' in constructor 1111:', window['air']);
this.uploadUrl = (<any> window).courseware.uploadUrl();
this.uploadData = (<any> window).courseware.uploadData();
window['air'].getUploadCallback = (url, data) => {
this.uploadUrl = url;
this.uploadData = data;
};
this.setUploadUrl();
}
setUploadUrl() {
if (!this.uploadUrl) {
setTimeout(() => {
this.setUploadUrl();
}, 500);
} else {
if (this._data) {
this._item = this._data;
this.refreshSafeUrl()
}
}
}
safeUrl;
refreshSafeUrl(formUrl=null) {
if (!formUrl) {
formUrl = this._item.formUrl || '';
}
this.safeUrl = this.sanitizer.bypassSecurityTrustResourceUrl(formUrl);
}
ngOnChanges() {
}
isInit = false;
ngOnInit(): void {
console.log(' in ngOnInit ')
if (!this._item) {
this._item = {};
}
this.initListener();
}
templateArr = [];
async selectSubTemplate() {
if (this.templateArr.length == 0) {
await this.getAllTemplateData().then((data : []) => {
this.templateArr = data;
});
}
this.isShowTemplate = true;
}
deleteSubTemplate() {
console.log('in deleteFunc');
this.refreshSafeUrl('');
this.deleteFunc.emit({});
}
templateClick(template) {
console.log("template: ", template);
this._item = {};
const {name, last_version, form_url} = template;
const formUrl = `http://staging-teach.cdn.ireadabc.com/h5template/${name}/v${last_version}/${form_url}&key=${this.key}`
this._item.formUrl = formUrl;
this._item.template = template;
console.log('formUrl: ', formUrl)
this.refreshSafeUrl();
this.closePanel();
this.sendData();
console.log('this.safeUrl 2 : ', this.safeUrl)
// const key = this.getQueryVariable(formUrl, 'key');
// console.log("key: ", key);
// console.log("url: ", this._item['safeUrl']);
}
sendData() {
this.save.emit(this._item)
}
getQueryVariable(url, variable) {
var query = url.substring(1);
var vars = query.split("&");
for (var i=0;i<vars.length;i++) {
var pair = vars[i].split("=");
if(pair[0] == variable){return pair[1];}
}
return(false);
}
saveSubTemplate() {
}
initListener() {
const msgFunc = (e) => {
if (!this.iframe || !this.iframe.nativeElement || !this.iframe.nativeElement.contentWindow) {
console.log('this.iframe not exist');
window.removeEventListener('message', msgFunc);
return;
}
let msgData = e.data;
if (msgData.action === "getData") {
console.log("getData e: ", e);
if (this.iframe) {
console.log('ifram exist: ', this.iframe);
}
if (!msgData.urlParams) {
console.log(' !msgData.urlParams: ' , msgData)
return;
}
const key = this.getQueryVariable(msgData.urlParams, 'key');
if (key != this.key) {
console.log(' key != this.key ')
return;
}
const data = { msg: 'success', data: JSON.stringify(this._item.data)};
// iframeContent.postMessage( { action: 'getData', data: JSON.stringify(data) }, '*');
this.iframe.nativeElement.contentWindow.postMessage( { action: 'getData', data: JSON.stringify(data) }, '*');
}
if (msgData.action === "setData") {
if (!msgData.urlParams) {
console.log(' !msgData.urlParams: ' , msgData)
return;
}
const key = this.getQueryVariable(msgData.urlParams, 'key');
if (key != this.key) {
console.log(' key != this.key ')
return;
}
console.log("setData e: ", e);
if (typeof(msgData.data) === "string") {
console.log('msgData is string');
msgData.data = JSON.parse(msgData.data);
console.log('msgData.data: ', msgData.data);
}
this._item.data = msgData.data;
this.sendData();
}
if (msgData.action === "getUpload") {
console.log( ' in getUpload')
// const iframeContent = this.el.nativeElement.querySelector('#iframe').contentWindow;
this.iframe.nativeElement.contentWindow.postMessage({
action: 'getUpload',
uploadUrl: this.uploadUrl,
uploadData: this.uploadData
}, '*');
}
}
if (this.iframe?.nativeElement?.contentWindow) {
console.log(' init Listener !');
window.addEventListener('message', msgFunc);
}
}
async getAllTemplateData() {
return new Promise((resolve, reject) => {
const c = window['courseware']
try {
if (c && c.getTemplates) {
c.getTemplates((data) => {
// data =
// [
// {"id": 26, "uuid":"c94fb1b0-3395-42ca-9ae3-03ca7c242d3f", "name": "hw_001", "description": "音频课件", "cover": "http://iplayabc-teach-yun.oss-cn-beijing.aliyuncs.com/3feddb01-4350-42ad-909f-236ad052fbb2.jpg", "type": 1, "t_type": 2, "form_url": "index.html?type=form", "play_url": "index.html", "last_version": 16},
// {"id": 27, "uuid":"b8bedc68-9ef6-42a6-ba99-513daa244a6d", "name": "hw_002", "description": "视频课件", "cover": "http://iplayabc-teach-yun.oss-cn-beijing.aliyuncs.com/1b11d370-9d8b-4358-98d3-4757f825d2a4.jpg", "type": 1, "t_type": 2, "form_url": "index.html?type=form", "play_url": "index.html", "last_version": 16},
// {"id": 28, "uuid":"068fcffb-4e99-4148-bc8e-2a89ee9bfe7c", "name": "hw_003", "description": "转盘课件", "cover": "http://iplayabc-teach-yun.oss-cn-beijing.aliyuncs.com/de3d8498-7523-4601-934e-932ac219a408.jpg", "type": 1, "t_type": 2, "form_url": "index.html?type=form", "play_url": "index.html", "last_version": 17},
// {"id": 29, "uuid":"b2bfd8df-e0e4-41c9-b65f-12160628e1a3", "name": "hw_004", "description": "连线课件", "cover": "http://iplayabc-teach-yun.oss-cn-beijing.aliyuncs.com/824989ac-78fb-4db7-848a-790cba00effe.jpg", "type": 1, "t_type": 2, "form_url": "index.html?type=form", "play_url": "index.html", "last_version": 9 },
// {"id": 30, "uuid":"d7be3c21-fc25-482d-a4a6-1a371970457a", "name": "hw_005", "description": "分类课件", "cover": "http://iplayabc-teach-yun.oss-cn-beijing.aliyuncs.com/aa69d85a-3430-48b2-91bf-a804dee1826d.jpg", "type": 1, "t_type": 2, "form_url": "index.html?type=form", "play_url": "index.html", "last_version": 19},
// {"id": 31, "uuid":"34e98c65-f3f0-46e4-a7b8-5caf72954847", "name": "hw_006", "description": "翻卡游戏", "cover": "http://iplayabc-teach-yun.oss-cn-beijing.aliyuncs.com/fe54031b-7879-4c65-8de7-102fa5e5e9f5.png", "type": 1, "t_type": 2, "form_url": "index.html?type=form", "play_url": "index.html", "last_version": 5 }
// ]
// resolve(data);
// return
if (data) {
console.log('data~~~:', data);
resolve(JSON.parse(data));
} else {
console.log('data none');
resolve([]);
}
})
}
} catch (error) {
reject(error);
}
})
// const data =
// [
// {"id": 26, "uuid":"c94fb1b0-3395-42ca-9ae3-03ca7c242d3f", "name": "hw_001", "description": "音频课件", "cover": "http://iplayabc-teach-yun.oss-cn-beijing.aliyuncs.com/3feddb01-4350-42ad-909f-236ad052fbb2.jpg", "type": 1, "t_type": 2, "form_url": "index.html?type=form", "play_url": "index.html", "last_version": 16},
// {"id": 27, "uuid":"b8bedc68-9ef6-42a6-ba99-513daa244a6d", "name": "hw_002", "description": "视频课件", "cover": "http://iplayabc-teach-yun.oss-cn-beijing.aliyuncs.com/1b11d370-9d8b-4358-98d3-4757f825d2a4.jpg", "type": 1, "t_type": 2, "form_url": "index.html?type=form", "play_url": "index.html", "last_version": 16},
// {"id": 28, "uuid":"068fcffb-4e99-4148-bc8e-2a89ee9bfe7c", "name": "hw_003", "description": "转盘课件", "cover": "http://iplayabc-teach-yun.oss-cn-beijing.aliyuncs.com/de3d8498-7523-4601-934e-932ac219a408.jpg", "type": 1, "t_type": 2, "form_url": "index.html?type=form", "play_url": "index.html", "last_version": 17},
// {"id": 29, "uuid":"b2bfd8df-e0e4-41c9-b65f-12160628e1a3", "name": "hw_004", "description": "连线课件", "cover": "http://iplayabc-teach-yun.oss-cn-beijing.aliyuncs.com/824989ac-78fb-4db7-848a-790cba00effe.jpg", "type": 1, "t_type": 2, "form_url": "index.html?type=form", "play_url": "index.html", "last_version": 9 },
// {"id": 30, "uuid":"d7be3c21-fc25-482d-a4a6-1a371970457a", "name": "hw_005", "description": "分类课件", "cover": "http://iplayabc-teach-yun.oss-cn-beijing.aliyuncs.com/aa69d85a-3430-48b2-91bf-a804dee1826d.jpg", "type": 1, "t_type": 2, "form_url": "index.html?type=form", "play_url": "index.html", "last_version": 19},
// {"id": 31, "uuid":"34e98c65-f3f0-46e4-a7b8-5caf72954847", "name": "hw_006", "description": "翻卡游戏", "cover": "http://iplayabc-teach-yun.oss-cn-beijing.aliyuncs.com/fe54031b-7879-4c65-8de7-102fa5e5e9f5.png", "type": 1, "t_type": 2, "form_url": "index.html?type=form", "play_url": "index.html", "last_version": 5 }
// ]
// return data;
}
/**
* 刷新 渲染页面
*/
refresh() {
// this.refreshEmitter.emit();
setTimeout(() => {
this.appRef.tick();
}, 1);
}
closePanel() {
console.log(' in closePanel ');
// this.refresh();
this.isShowTemplate = false;
}
panelCancelClicked() {
this.isShowTemplate = false;
}
panelOkClicked() {
this.isShowTemplate = false;
}
ngOnDestroy() {
}
}
<div class="position-relative">
<button nz-button (click)="setAnimaBtnClick()" style=" border-radius: 0.5rem; border: 1px solid #ddd;">
<i nz-icon nzType="tool" nzTheme="outline"></i>
{{btnName}}
</button>
<!--配置龙骨面板-->
<nz-modal [(nzVisible)]="animaPanelVisible" (nzAfterClose)="closePanel()" nzTitle="配置资源文件"
(nzOnCancel)="animaPanelCancel()" (nzOnOk)="animaPanelOk()" nzOkText="保存">
<div class="anima-upload-btn">
<span style="margin-right: 10px">上传 ske_json 文件: </span>
<nz-upload [nzShowUploadList]="false" nzAccept="application/json" [nzAction]="uploadUrl" [nzData]="uploadData"
(nzChange)="skeJsonHandleChange($event)">
<button nz-button><i nz-icon nzType="upload"></i><span>Upload</span></button>
</nz-upload>
<i *ngIf="isSkeJsonLoading" style="margin-left: 10px;" nz-icon [nzType]="'loading'"></i>
<span *ngIf="skeJsonData && skeJsonData['name']" style="margin-left: 10px"><u> {{skeJsonData['name']}} </u></span>
</div>
<div class="anima-upload-btn">
<span style="margin-right: 10px">上传 tex_json 文件: </span>
<nz-upload [nzShowUploadList]="false" nzAccept="application/json" [nzAction]="uploadUrl" [nzData]="uploadData"
(nzChange)="texJsonHandleChange($event)">
<button nz-button><i nz-icon nzType="upload"></i><span>Upload</span></button>
</nz-upload>
<i *ngIf="isTexJsonLoading" style="margin-left: 10px;" nz-icon [nzType]="'loading'"></i>
<span *ngIf="texJsonData && texJsonData['name']" style="margin-left: 10px"><u> {{texJsonData['name']}} </u></span>
</div>
<div class="anima-upload-btn">
<span style="margin-right: 10px">上传 tex_png 文件: </span>
<nz-upload [nzShowUploadList]="false" nzAccept="image/*" [nzAction]="uploadUrl" [nzData]="uploadData"
(nzChange)="texPngHandleChange($event)">
<button nz-button><i nz-icon nzType="upload"></i><span>Upload</span></button>
</nz-upload>
<i *ngIf="isTexPngLoading" style="margin-left: 10px;" nz-icon [nzType]="'loading'"></i>
<span *ngIf="texPngData && texPngData['name']" style="margin-left: 10px"><u> {{texPngData['name']}} </u></span>
</div>
<div class="anima-upload-btn" *ngIf="animaNames && animaNames.length > 0">
提示:需包含动画: {{animaNames.toString()}}.
</div>
</nz-modal>
</div>
\ No newline at end of file
@import '../../style/common_mixin.css';
.p-image-uploader {
position: relative;
display: block;
width: 100%;
height: 0;
padding-bottom: 56.25%;
.p-box {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
border: 2px dashed #ddd;
border-radius: 0.5rem;
background-color: #fafafa;
text-align: center;
color: #aaa;
.p-upload-icon {
text-align: center;
margin: auto;
.anticon-upload {
color: #888;
font-size: 5rem;
}
.p-progress-bar {
position: relative;
width: 20rem;
height: 1.5rem;
border: 1px solid #ccc;
border-radius: 1rem;
.p-progress-bg {
background-color: #1890ff;
border-radius: 1rem;
height: 100%;
}
.p-progress-value {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
text-shadow: 0 0 4px #000;
color: white;
text-align: center;
font-size: 0.9rem;
line-height: 1.5rem;
}
}
}
.p-preview {
width: 100%;
height: 100%;
background-size: contain;
background-repeat: no-repeat;
background-position: 50% 50%;
//background-image: url("https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png");
}
}
.d-flex{
display: flex;
}
}
.p-btn-delete {
position: absolute;
right: -0.5rem;
top: -0.5rem;
width: 2rem;
height: 2rem;
border: 0.2rem solid white;
border-radius: 50%;
font-size: 1.2rem;
background-color: #fb781a;
color: white;
text-align: center;
}
.p-upload-progress-bg {
position: absolute;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
& .i-text {
position: absolute;
text-align: center;
color: white;
text-shadow: 0 0 2px rgba(0, 0, 0, .85);
}
& .i-bg {
position: absolute;
left: 0;
top: 0;
height: 100%;
background-color: #27b43f;
border-radius: 0.5rem;
}
}
.anima-upload-btn {
padding: 10px;
}
:host ::ng-deep .ant-upload {
display: block;
}
import {ApplicationRef, Component, EventEmitter, Input, OnChanges, OnDestroy, Output} from '@angular/core';
import { NzMessageService, UploadXHRArgs, UploadFile } from 'ng-zorro-antd';
@Component({
selector: 'app-upload-dragon-bone',
templateUrl: './upload-dragon-bone.component.html',
styleUrls: ['./upload-dragon-bone.component.scss']
})
export class UploadDragonBoneComponent implements OnDestroy, OnChanges {
uploading = false;
progress = 0;
@Input()
btnName = '配置龙骨动画';
@Input()
animaNames = [];
@Input()
skeJsonData = {};
@Input()
texJsonData = {};
@Input()
texPngData = {};
@Output()
save = new EventEmitter();
@Output()
refreshEmitter = new EventEmitter();
// @Input()
// picUrl;
// @Input()
// canDelete = false;
// @Output()
// imageUploaded = new EventEmitter();
// @Output()
// imageUploadFailure = new EventEmitter();
// @Output()
// delete = new EventEmitter();
// @Input()
// picItem = null;
// @Input()
// iconSize = 2;
// @Input()
// TIP = 'Click here to upload image';
// @Input()
// disableUpload = false;
uploadUrl;
uploadData;
animaPanelVisible = false;
isSkeJsonLoading = false;
isTexJsonLoading = false;
isTexPngLoading = false;
constructor(private appRef: ApplicationRef, private nzMessageService: NzMessageService) {
this.uploadUrl = (<any> window).courseware.uploadUrl();
this.uploadData = (<any> window).courseware.uploadData();
window['air'].getUploadCallback = (url, data) => {
this.uploadUrl = url;
this.uploadData = data;
};
}
ngOnChanges() {
}
setAnimaBtnClick() {
this.animaPanelVisible = true;
this.refresh();
}
animaPanelCancel() {
this.animaPanelVisible = false;
this.refresh();
}
animaPanelOk() {
this.sendItemDragonBoneData();
this.animaPanelVisible = false;
this.refresh();
}
sendItemDragonBoneData() {
const data = {};
data['skeJsonData'] = this.skeJsonData;
data['texJsonData'] = this.texJsonData;
data['texPngData'] = this.texPngData;
this.save.emit(data);
}
skeJsonHandleChange(e) {
console.log('e: ', e);
switch (e.type) {
case 'start':
this.isSkeJsonLoading = true;
break;
case 'success':
this.skeJsonData['url'] = e.file.response.url;
this.skeJsonData['name'] = e.file.name;
this.nzMessageService.success('上传成功');
this.isSkeJsonLoading = false;
break;
case 'progress':
break;
}
}
texJsonHandleChange(e) {
console.log('e: ', e);
switch (e.type) {
case 'start':
this.isTexJsonLoading = true;
break;
case 'success':
this.texJsonData['url'] = e.file.response.url;
this.texJsonData['name'] = e.file.name;
this.nzMessageService.success('上传成功');
this.isTexJsonLoading = false;
break;
case 'progress':
break;
}
}
texPngHandleChange(e) {
console.log('e: ', e);
switch (e.type) {
case 'start':
this.isTexPngLoading = true;
break;
case 'success':
this.texPngData['url'] = e.file.response.url;
this.texPngData['name'] = e.file.name;
this.nzMessageService.success('上传成功');
this.isTexPngLoading = false;
break;
case 'progress':
break;
}
}
/**
* 刷新 渲染页面
*/
refresh() {
// this.refreshEmitter.emit();
setTimeout(() => {
this.appRef.tick();
}, 1);
}
closePanel() {
console.log(' in closePanel ');
this.refresh();
}
ngOnDestroy() {
}
}
......@@ -42,7 +42,28 @@ export class UploadImageWithPreviewComponent implements OnDestroy, OnChanges {
this.uploadData = data;
};
this.setUploadUrl();
}
setUploadUrl() {
if (!this.uploadUrl) {
console.log('this.uploadUrl -=- 1 : ', this.uploadUrl)
this.uploadUrl = (<any> window).courseware.uploadUrl();
this.uploadData = (<any> window).courseware.uploadData();
console.log('this.uploadUrl -=- 2 : ', this.uploadUrl)
setTimeout(() => {
this.setUploadUrl();
}, 500);
}
console.log('this.uploadUrl -=- 3 : ', this.uploadUrl)
}
ngOnChanges() {
if (!this.picItem) {
return;
......
......@@ -81,8 +81,8 @@
</div>
</div>
<div class="p-preview" *ngIf="!uploading && videoUrl " >
<!--<video crossorigin="anonymous" [src]="videoUrl" controls #videoNode></video>-->
<video [src]="safeVideoUrl(videoUrl)" controls #videoNode></video>
<video crossorigin="anonymous" [src]="videoUrl" controls #videoNode></video>
<!-- <video [src]="safeVideoUrl(videoUrl)" controls #videoNode></video> -->
</div>
</div>
<div [style.display]="!checkVideoExists?'none':''">
......
......@@ -6,3 +6,30 @@
height: 100%;
}
.radioPaire {
float: left;
margin: 3px;
border-style: dashed;
border-color: #000;
border-width: 1px;
}
.border {
border-radius: 20px;
border-style: dashed;
padding: 20px;
margin: 20px;
/*width: 500px; */
/*//border-radius: 20px;*/
/*//border-width: 2px;*/
/*//border-color: #000000;*/
}
.border-lite {
border: 2px dashed #ddd;
border-radius: 0.5rem;
padding: 10px;
margin: 10px;
}
<div class="model-content">
<div style="position: absolute; left: 200px; top: 100px; width: 800px;">
<div style="padding: 20px;">
<input type="text" nz-input [(ngModel)]="item.text" (blur)="save()">
<div style="width: 400px; display: flex; justify-content: center; margin-bottom: 50px;">
<h2 style="width: 60px;">标题:</h2>
<input type="text" nz-input [(ngModel)]="item.title" (blur)="save()" style="margin-left: 15px">
</div>
<h2>热区配置:</h2>
<app-custom-hot-zone
[bgItem]="item.bgItem"
[hotZoneItemArr]="item.hotZoneItemArr"
[customTypeGroupArr]="customTypeGroupArr"
(save)="saveHotZone(item, $event)"
>
</app-custom-hot-zone>
<div>
<nz-radio-group [(ngModel)]="item.ques_type" style="margin-top: 50px" (ngModelChange)="save()">
<h2 nz-radio nzValue="single_choice">选择题</h2>
<h2 nz-radio nzValue="short_answer">简答题</h2>
</nz-radio-group>
</div>
<!-- 选择题 -->
<div *ngIf="item.ques_type == 'single_choice'">
<div style="margin-top: 30px; display: flex; width: 1000px;">
<div style="width: 300px; margin-top: 10px; margin-right: 15px;">
<span style="margin-right: 5px;">问题:</span>
<app-upload-image-with-preview
[picUrl]="item.ques_pic_url"
(imageUploaded)="onImageUploadSuccess($event, 'ques_pic_url')">
</app-upload-image-with-preview>
</div>
<div style="width: 300px;">
<div style="display: flex; align-items: center; height: 30px;">
<span style="margin-right: 5px; margin-left: 15px;">小标题:</span>
<app-audio-recorder
[audioUrl]="item.title_audio_url"
(audioUploaded)="onAudioUploadSuccess($event, 'title_audio_url')"
></app-audio-recorder>
</div>
<app-upload-image-with-preview
[picUrl]="item.title_pic_url"
(imageUploaded)="onImageUploadSuccess($event, 'title_pic_url')">
</app-upload-image-with-preview>
</div>
</div>
<div *ngFor="let op of item.optionArr" style="border: 1px solid #ccc; margin: 10px; padding: 10px; border-radius: 5px;">
<div style="margin-top: 20px; display: flex; align-items: center; width: 500px;">
<span style="margin-right: 5px;">{{op.id}}:</span>
<div style="width: 300px;">
<app-upload-image-with-preview
[picUrl]="op.pic_url"
(imageUploaded)="onImageUploadSuccess($event, 'pic_url', op)">
</app-upload-image-with-preview>
</div>
<div style="margin-left: 20px;">
<span> 正确: </span>
<nz-switch [(ngModel)]="op.is_right" (ngModelChange)="save()"></nz-switch>
</div>
</div>
<nz-divider style="margin-top: 20px;"></nz-divider>
<div *ngIf="!op.is_right" style="padding: 20px; display: flex; align-items: center;">
<div style="width: 300px;">
纠错视频:
<app-upload-video
(videoUploaded)="onVideoUploadSuccess($event, op)"
[item]="op"
[videoUrl]="op.video_url">
</app-upload-video>
</div>
<div style="margin-left: 20px;">
<div style="display: flex; align-items: center;">
<span style="margin-right: 5px;">小标题2:</span>
<app-audio-recorder
[audioUrl]="op.title_audio_url"
(audioUploaded)="onAudioUploadSuccess($event, 'title_audio_url', op)"
></app-audio-recorder>
</div>
<div style="width: 300px;">
<app-upload-image-with-preview
[picUrl]="op.title_pic_url"
(imageUploaded)="onImageUploadSuccess($event, 'title_pic_url', op)">
</app-upload-image-with-preview>
</div>
<span style="margin-left: 15px;" >问题2:</span>
<div style="width: 300px;">
<app-upload-image-with-preview
[picUrl]="op.ques_pic_url"
(imageUploaded)="onImageUploadSuccess($event, 'ques_pic_url', op)">
</app-upload-image-with-preview>
</div>
</div>
<div style="margin-left: 20px;">
<div *ngFor="let op2 of op.optionArr" style="display: flex; align-items: center; margin-top: 5px;">
{{op2.id}}
<div style="width: 150px; margin-left: 5px;">
<app-upload-image-with-preview
[picUrl]="op2.pic_url"
(imageUploaded)="onImageUploadSuccess($event, 'pic_url', op2)">
</app-upload-image-with-preview>
</div>
<div style="margin-left: 5px;">
<span> 正确: </span>
<nz-switch [(ngModel)]="op2.is_right" (ngModelChange)="save()"></nz-switch>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 简答题 -->
<div *ngIf="item.ques_type == 'short_answer'">
<div style="margin-top: 30px; display: flex; width: 1000px;">
<div style="width: 300px; margin-top: 10px; margin-right: 15px;">
<span style="margin-right: 5px;">选择题问题:</span>
<app-upload-image-with-preview
[picUrl]="item.choice_ques_pic_url"
(imageUploaded)="onImageUploadSuccess($event, 'choice_ques_pic_url')">
</app-upload-image-with-preview>
</div>
<div style="width: 300px;">
<div style="display: flex; align-items: center; height: 30px;">
<span style="margin-right: 5px; margin-left: 15px;">选择小标题:</span>
<app-audio-recorder
[audioUrl]="item.choice_title_audio_url"
(audioUploaded)="onAudioUploadSuccess($event, 'choice_title_audio_url')"
></app-audio-recorder>
</div>
<app-upload-image-with-preview
[picUrl]="item.choice_title_pic_url"
(imageUploaded)="onImageUploadSuccess($event, 'choice_title_pic_url')">
</app-upload-image-with-preview>
</div>
</div>
<div *ngFor="let op of item.optionArr" style="border: 1px solid #ccc; margin: 10px; padding: 10px; border-radius: 5px;">
<div style="margin-top: 20px; display: flex; align-items: center; width: 500px;">
<span style="margin-right: 5px;">{{op.id}}:</span>
<div style="width: 300px;">
<app-upload-image-with-preview
[picUrl]="op.pic_url"
(imageUploaded)="onImageUploadSuccess($event, 'pic_url', op)">
</app-upload-image-with-preview>
</div>
<div style="margin-left: 20px;">
<span> 正确: </span>
<nz-switch [(ngModel)]="op.is_right" (ngModelChange)="save()"></nz-switch>
</div>
</div>
<nz-divider style="margin-top: 20px;"></nz-divider>
</div>
<nz-divider style="margin-top: 50px;"></nz-divider>
<div style="display: flex; align-items: center; margin-bottom: 50px;">
<div style="width: 400px; margin-top: 10px; margin-right: 15px;">
<span style="margin-right: 5px;">新简答问题:</span>
<app-upload-image-with-preview
[picUrl]="item.answer_ques_pic_url"
(imageUploaded)="onImageUploadSuccess($event, 'answer_ques_pic_url')">
</app-upload-image-with-preview>
</div>
<div style="width: 400px;">
<div style="display: flex; align-items: center; height: 30px;">
<span style="margin-right: 5px; margin-left: 15px;">新简答小标题:</span>
<app-audio-recorder
[audioUrl]="item.answer_title_audio_url"
(audioUploaded)="onAudioUploadSuccess($event, 'answer_title_audio_url')"
></app-audio-recorder>
</div>
<app-upload-image-with-preview
[picUrl]="item.answer_title_pic_url"
(imageUploaded)="onImageUploadSuccess($event, 'answer_title_pic_url')">
</app-upload-image-with-preview>
</div>
<div style="width: 400px; margin-left: 15px;">
<span style="margin-right: 5px; ">新简答题 答案:</span>
<div style="width: 200px; display: flex; justify-content: center; align-items: center;">
<input type="text" nz-input [(ngModel)]="item.new_answer" (blur)="save()" style="margin-top: 5px">
</div>
</div>
</div>
</div>
<!--
<div style="width: 100%; margin-top: 15px; margin-bottom: 50px;" align="center">
<h2>气泡文本</h2>
<div style="width: 80%; display: flex; justify-content: center; align-items: center;">
<input type="text" nz-input [(ngModel)]="item.title" (blur)="save()" style="margin-top: 5px">
</div>
</div>
<app-custom-hot-zone
[bgItem]="item.bgItem"
[hotZoneItemArr]="item.hotZoneItemArr"
[customTypeGroupArr]="customTypeGroupArr"
(save)="saveHotZone(item, $event)"
>
</app-custom-hot-zone> -->
<!-- <div style="width: 300px;" align='center'>
<span>图1: </span>
<app-upload-image-with-preview
[picUrl]="item.pic_url"
(imageUploaded)="onImageUploadSuccess($event, 'pic_url')"
></app-upload-image-with-preview>
(imageUploaded)="onImageUploadSuccess($event, 'pic_url')">
</app-upload-image-with-preview>
</div>
<div style="width: 300px; margin-top: 5px;" align='center'>
<span>图2: </span>
<app-upload-image-with-preview
[picUrl]="item.pic_url_2"
(imageUploaded)="onImageUploadSuccess($event, 'pic_url_2')">
</app-upload-image-with-preview>
</div>
<div style="width: 300px; margin-top: 15px;">
<span>文本: </span>
<input type="text" nz-input [(ngModel)]="item.text" (blur)="save()">
</div>
<div style="margin-top: 5px">
<span>音频: </span>
<app-audio-recorder
[audioUrl]="item.audio_url"
(audioUploaded)="onAudioUploadSuccess($event, 'audio_url')"
></app-audio-recorder>
<app-custom-hot-zone></app-custom-hot-zone>
<app-upload-video></app-upload-video>
<app-lesson-title-config></app-lesson-title-config>
</div>
</div> -->
</div>
</div>
\ No newline at end of file
import {Component, EventEmitter, Input, OnDestroy, OnChanges, OnInit, Output, ApplicationRef, ChangeDetectorRef} from '@angular/core';
import { Component, EventEmitter, Input, OnDestroy, OnChanges, OnInit, Output, ApplicationRef, ChangeDetectorRef } from '@angular/core';
import { JsonPipe } from '@angular/common';
@Component({
......@@ -10,47 +10,154 @@ import {Component, EventEmitter, Input, OnDestroy, OnChanges, OnInit, Output, Ap
export class FormComponent implements OnInit, OnChanges, OnDestroy {
// 储存数据用
saveKey = "test_0011";
saveKey = "JM_MATH01";
// 储存对象
item;
customTypeGroupArr = [
{
name: '轮播图片',
audio: true,
pic: true,
// rect: true,
// isShowPos: true,
// isCopy: true,
// label: '比对',
},
{
name: '选择区域',
rect: true,
},
{
name: '简答问题',
pic: true,
},
{
name: '简答填空区',
rect: true,
label: '答案',
},
]
constructor(private appRef: ApplicationRef, private changeDetectorRef: ChangeDetectorRef) {
constructor(private appRef: ApplicationRef,private changeDetectorRef: ChangeDetectorRef) {
}
createShell() {
this.item.wordList.push({
word: '',
audio: '',
backWord: '',
backWordAudio: '',
});
this.save();
}
removeShell(idx) {
this.item.wordList.splice(idx, 1);
this.save();
}
ngOnInit() {
this.item = {};
// 获取存储的数据
(<any> window).courseware.getData((data) => {
(<any>window).courseware.getData((data) => {
if (data) {
this.item = data;
}
console.log('this.item: ', this.item);
this.init();
this.changeDetectorRef.markForCheck();
this.changeDetectorRef.detectChanges();
this.refresh();
}, this.saveKey);
}
ngOnChanges() {
}
ngOnDestroy() {
}
init() {
if (!this.item.sentenceArr) {
this.item.sentenceArr = [];
}
if (!this.item.templateArr) {
this.item.templateArr = [];
}
init() {
if (!this.item.optionArr) {
this.item.optionArr = [
{id: 'A', optionArr: [{id: 'A'}, {id: 'B'}]},
{id: 'B', optionArr: [{id: 'A'}, {id: 'B'}]},
{id: 'C', optionArr: [{id: 'A'}, {id: 'B'}]},
{id: 'D', optionArr: [{id: 'A'}, {id: 'B'}]},
];
}
if (!this.item.ques_type) {
this.item.ques_type = 'single_choice'
}
}
addSubTemplate() {
this.item.templateArr.push({
})
this.save();
}
onDeleteTemplate(e, index) {
this.item.templateArr[index] = {};
this.item.templateArr.splice(index, 1);
this.save();
}
onSaveTemplate(e, index) {
this.item.templateArr[index] = e;
this.save();
}
addBtnClick() {
this.item.sentenceArr.push({});
this.save();
}
deleteBtnClick(index) {
this.item.sentenceArr.splice(index, 1);
this.save();
}
onSaveCustomAction(e) {
console.log('e:', e);
this.item.customAction = e;
this.save();
}
saveHotZone(group, e) {
console.log('e: ', e);
const {bgItem, hotZoneItemArr} = e;
group.bgItem = bgItem;
group.hotZoneItemArr = hotZoneItemArr;
this.save();
}
......@@ -58,9 +165,12 @@ export class FormComponent implements OnInit, OnChanges, OnDestroy {
* 储存图片数据
* @param e
*/
onImageUploadSuccess(e, key) {
onImageUploadSuccess(e, key, item=null) {
if (!item) {
item = this.item;
}
this.item[key] = e.url;
item[key] = e.url;
this.save();
}
......@@ -68,20 +178,42 @@ export class FormComponent implements OnInit, OnChanges, OnDestroy {
* 储存音频数据
* @param e
*/
onAudioUploadSuccess(e, key) {
onAudioUploadSuccess(e, key, item=null) {
if (item == null) {
item = this.item;
}
item[key] = e.url;
this.save();
}
this.item[key] = e.url;
onWordAudioUploadSuccess(e, idx) {
this.item.wordList[idx].audio = e.url;
this.save();
}
onBackWordAudioUploadSuccess(e, idx) {
this.item.wordList[idx].backWordAudio = e.url;
this.save();
}
onVideoUploadSuccess(e, item=null) {
console.log(' in onVideoUploadSuccess')
if (!item) {
item = this.item;
}
item.video_url = e.url;
this.save();
}
/**
* 储存数据
*/
save() {
(<any> window).courseware.setData(this.item, null, this.saveKey);
(<any>window).courseware.setData(this.item, null, this.saveKey);
this.refresh();
console.log('this.item = ' + JSON.stringify(this.item));
}
/**
......@@ -94,4 +226,3 @@ export class FormComponent implements OnInit, OnChanges, OnDestroy {
}
}
\ No newline at end of file
import TWEEN from '@tweenjs/tween.js';
import { randomInt } from 'crypto';
interface AirWindow extends Window {
air: any;
......@@ -77,7 +78,9 @@ export class MySprite extends Sprite {
_offCtx;
isCircleStyle = false; // 切成圆形
parent;
_maskSpr;
_maskSprArr = [];
_maskType = "destination-in" // destination-out
init(imgObj = null, anchorX: number = 0.5, anchorY: number = 0.5) {
......@@ -89,6 +92,8 @@ export class MySprite extends Sprite {
this.width = this.img.width;
this.height = this.img.height;
// this.img.setAttribute("crossOrigin",'Anonymous')
}
this.anchorX = anchorX;
......@@ -114,16 +119,20 @@ export class MySprite extends Sprite {
this._radius = r;
}
setMaskSpr(spr) {
this._maskSpr = spr;
addMaskSpr(spr) {
this._maskSprArr.push(spr);
this._createOffCtx();
}
setMaskType(t) {
this._maskType = t;
}
_createOffCtx() {
if (!this._offCtx) {
this._offCanvas = document.createElement('canvas'); // 创建一个新的canvas
this.width = this._offCanvas.width = this.width; // 创建一个正好包裹住一个粒子canvas
this.height = this._offCanvas.height = this.height;
this._offCanvas.width = this.width; // 创建一个正好包裹住一个粒子canvas
this._offCanvas.height = this.height;
this._offCtx = this._offCanvas.getContext('2d');
}
}
......@@ -220,12 +229,22 @@ export class MySprite extends Sprite {
this._offCtx.clearRect(0, 0, this.width, this.height);
this._offCtx.drawImage(this.img, 0, 0);
if (this._maskSpr) {
this._offCtx.globalCompositeOperation = 'destination-in';
this._offCtx.drawImage(this._maskSpr.img, this._maskSpr.x + this._maskSpr._offX - this._offX , this._maskSpr.y + this._maskSpr._offY - this._offY);
this._offCtx.globalCompositeOperation = this._maskType;
if (this._maskSprArr && this._maskSprArr.length > 0) {
for (let i=0; i<this._maskSprArr.length; i++) {
this._maskSprArr[i].ctx = this._offCtx;
this._maskSprArr[i].update();
}
}
this.ctx.drawImage(this._offCanvas, this._offX, this._offY);
this._offCtx.restore();
......@@ -293,6 +312,8 @@ export class MySprite extends Sprite {
child.alpha = this.alpha;
}
child.ctx = this.ctx;
}
removeChild(child) {
const index = this.children.indexOf(child);
......@@ -567,6 +588,7 @@ export class Label extends MySprite {
// fontSize:String = '40px';
fontName = 'Verdana';
textAlign = 'left';
textBaseline = 'middle';
fontSize = 40;
fontColor = '#000000';
fontWeight = 900;
......@@ -596,7 +618,7 @@ export class Label extends MySprite {
this.ctx.font = `${this.fontSize}px ${this.fontName}`;
this.ctx.textAlign = this.textAlign;
this.ctx.textBaseline = 'middle';
this.ctx.textBaseline = this.textBaseline;
this.ctx.fontWeight = this.fontWeight;
this._width = this.ctx.measureText(this.text).width;
......@@ -819,11 +841,18 @@ export class RichTextOld extends Label {
export class RichText extends Label {
disH = 30;
topH = 0;
disH = 10;
offW = 10;
row = 1;
textBaseline = "middle";
isShowWordBg = false;
wordBgData;
constructor(ctx?: any) {
super(ctx);
......@@ -831,7 +860,93 @@ export class RichText extends Label {
// this.dataArr = dataArr;
}
getLineNum() {
this.drawSelf();
return this.row;
}
getAreaHeight() {
this.drawSelf();
const tmpTextH = this.row * this.fontSize;
const tmpDisH = (this.row - 1) * this.disH
return tmpTextH + tmpDisH;
}
getSubTextRectGroup(text, targetIndex = 0) {
console.log('!!!wordBgData: ', this.wordBgData);
const rectGroup = [];
const subTextArr = text.split(' ');
let baseIndex = targetIndex;
console.log('subTextArr: ', subTextArr);
for (let i=0; i<subTextArr.length; i++) {
const subText = subTextArr[i];
if (!subText) {
continue;
}
const subData = this.getSubTextRect(subText, baseIndex)
if (subData) {
console.log('baseIndex1 : ', baseIndex);
rectGroup.push(subData);
baseIndex = Number( subData.index ) + subData.text.length;
console.log('baseIndex2 : ', baseIndex);
}
}
return rectGroup;
}
getSubTextRect(subText, targetIndex=0) {
subText = subText.trim();
if (!subText) {
return;
}
this.isShowWordBg = true;
this.update();
const tmpLabel = new RichText();
tmpLabel.fontSize = this.fontSize;
tmpLabel.fontName = this.fontName;
tmpLabel.textAlign = this.textAlign;
tmpLabel.textBaseline = this.textBaseline;
tmpLabel.fontWeight = this.fontWeight;
tmpLabel.width = this.width;
tmpLabel.height = this.height;
// console.log('subText: ', subText);
// console.log('this.text: ', this.text);
// console.log('targetIndex: ', targetIndex);
// const indexArr = searchSubStr(this.text, subText);
// console.log('indexArr: ', indexArr);
// const index = indexArr[targetIndex];
const index = this.text.indexOf(subText, targetIndex);
// console.log('index: ', index);
if (index == -1) {
return;
}
// console.log('this.wordBgData: ', this.wordBgData);
// const index = this.text.indexOf(subText);
// console.log('!!!index: ', index);
const data = this.wordBgData[index.toString()];
// console.log('!!!wordBgData: ', this.wordBgData);
console.log('!!!data: ', data);
return data;
}
drawText() {
......@@ -839,12 +954,24 @@ export class RichText extends Label {
return;
}
let curCtx = this.ctx;
if (this._offCtx) {
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;
this._offCtx.save();
this._offCtx.clearRect(0, 0, this.width, this.height);
curCtx = this._offCtx;
}
curCtx.font = `${this.fontSize}px ${this.fontName}`;
curCtx.textAlign = this.textAlign;
curCtx.textBaseline = this.textBaseline;
curCtx.fontWeight = this.fontWeight;
curCtx.fillStyle = this.fontColor;
const selfW = this.width * this.scaleX;
......@@ -856,39 +983,83 @@ export class RichText extends Label {
const w = selfW - this.offW * 2;
const disH = (this.fontSize + this.disH) * this.scaleY;
let isPushWordData = false;
if (this.isShowWordBg && !this.wordBgData) {
this.wordBgData = {};
isPushWordData = true;
}
let wordIndex = -1;
for (const c of chr) {
if (isPushWordData) {
}
for (const c of chr) {
if (this.ctx.measureText(temp).width < w && this.ctx.measureText(temp + (c)).width <= w) {
if (c == '\n') {
row.push(temp);
temp = '';
} else if (curCtx.measureText(temp).width < w && curCtx.measureText(temp + (c)).width <= w) {
temp += ' ' + c;
} else {
row.push(temp);
temp = ' ' + c;
}
if (isPushWordData) {
wordIndex = this.text.indexOf(c, wordIndex+1);
const key = wordIndex.toString();
const rectX = curCtx.measureText(temp).width
const rectY = ( row.length ) * disH / this.scaleY
const rectW = curCtx.measureText(c).width;
const rectH = this.fontSize;
this.wordBgData[key] = {rect: {x: rectX, y: rectY, width: rectW, height: rectH}, text: c, index: wordIndex}
}
}
row.push(temp);
this.row = row.length;
const x = 0;
const y = -row.length * disH / 2;
const y = this.topH//-row.length * disH / 2;
// for (let b = 0 ; b < row.length; b++) {
// this.ctx.strokeText(row[b], x, y + ( b + 1 ) * disH ); // 每行字体y坐标间隔20
// curCtx.strokeText(row[b], x, y + ( b + 1 ) * disH ); // 每行字体y坐标间隔20
// }
if (this._outlineFlag) {
this.ctx.lineWidth = this._outLineWidth;
this.ctx.strokeStyle = this._outLineColor;
curCtx.lineWidth = this._outLineWidth;
curCtx.strokeStyle = this._outLineColor;
for (let b = 0 ; b < row.length; b++) {
this.ctx.strokeText(row[b], x, y + ( b + 1 ) * disH ); // 每行字体y坐标间隔20
curCtx.strokeText(row[b], x, y + ( b + 0 ) * disH / this.scaleY ); // 每行字体y坐标间隔20
}
// this.ctx.strokeText(this.text, 0, 0);
// curCtx.strokeText(this.text, 0, 0);
}
// this.ctx.fillStyle = '#ff7600';
// curCtx.fillStyle = '#ff7600';
for (let b = 0 ; b < row.length; b++) {
this.ctx.fillText(row[b], x, y + ( b + 1 ) * disH ); // 每行字体y坐标间隔20
curCtx.fillText(row[b], x, y + ( b + 0 ) * disH / this.scaleY ); // 每行字体y坐标间隔20
}
if (this._offCtx) {
this._offCtx.globalCompositeOperation = this._maskType;
if (this._maskSprArr && this._maskSprArr.length > 0) {
for (let i=0; i<this._maskSprArr.length; i++) {
this._maskSprArr[i].ctx = this._offCtx;
this._maskSprArr[i].update();
}
// console.log('aaaaa1');
}
this.ctx.drawImage(this._offCanvas, this._offX, this._offY);
this._offCtx.restore();
}
}
......@@ -954,11 +1125,33 @@ export class ShapeRect extends MySprite {
drawShape() {
if (this._offCtx) {
this._offCtx.save();
this._offCtx.clearRect(0, 0, this.width, this.height);
this._offCtx.fillStyle = this.fillColor;
this._offCtx.fillRect(this._offX, this._offY, this.width, this.height);
this._offCtx.globalCompositeOperation = this._maskType;
if (this._maskSprArr && this._maskSprArr.length > 0) {
for (let i=0; i<this._maskSprArr.length; i++) {
this._maskSprArr[i].ctx = this._offCtx;
this._maskSprArr[i].update();
}
}
this.ctx.drawImage(this._offCanvas, this._offX, this._offY);
this._offCtx.restore();
} else {
this.ctx.fillStyle = this.fillColor;
this.ctx.fillRect(this._offX, this._offY, this.width, this.height);
}
}
drawSelf() {
super.drawSelf();
......@@ -1765,6 +1958,78 @@ export function showPopParticle(img, pos, parent, num = 20, minLen = 40, maxLen
}
export function RandomInt(a, b = 0) {
let max = Math.max(a, b);
let min = Math.min(a, b);
return Math.floor(Math.random() * (max - min) + min);
}
export function showShapeParticle(shapeType, shapeW, shapeH, pos, parent, num = 20, minLen = 40, maxLen = 80, showTime = 0.4) {
const randomColorArr = [
'#6996af',
'#4da940',
'#911b40',
]
for (let i = 0; i < num; i ++) {
let particle;
switch(shapeType) {
case 'rect':
particle = new ShapeRectNew();
particle.setSize(shapeW, shapeH, 0);
particle.fillColor = randomColorArr[RandomInt(0, randomColorArr.length)];
break;
}
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) {
......@@ -1835,6 +2100,8 @@ export class MyVideo extends MySprite {
this.width = this.element.videoWidth;
this.height = this.element.videoHeight;
console.log('this.width: ', this.width);
console.log('this.height: ', this.height);
this.element.currentTime = 0.01;
}
......@@ -1846,6 +2113,403 @@ export class MyVideo extends MySprite {
}
}
export class InputView extends MySprite {
element;
callback;
_isShowScroll = false;
constructor(ctx=null) {
super(ctx);
this._createInput()
}
set isShowScroll(v) {
this._isShowScroll = v;
if (!v) {
this.element.style.overflow = 'hidden';
}
}
get isShowScroll() {
return this._isShowScroll;
}
_createInput() {
const input = document.createElement('textarea');
input.style.resize = 'none';
input.style.border = 'none';
input.style.position = 'absolute';
input.onblur = this.onblur.bind(this);
const div = document.getElementById('div_input');
div.appendChild(input);
this.element = input;
}
onblur() {
if (this.callback) {
this.callback(this.element.value);
}
this.callback = null;
this.hide();
}
set text(v) {
this.element.value = v;
}
show() {
this.element.hidden = false;
setTimeout(() => {
this.element.focus();
}, 1);
}
hide() {
this.element.hidden = true;
}
setStyle(style) {
for ( let key in style ) {
console.log('key: ', key)
console.log('value: ', style[key])
this.element.style[key] = style[key];
}
console.log('this.element: ``````', this.element)
}
refreshInputStyle() {
this.element.style.left = this.x + 'px';
this.element.style.top = this.y + 'px';
this.element.style.width = this.width + 'px';
this.element.style.height = this.height + 'px';
}
}
export class ScrollView extends MySprite {
static ScrollSideType = {
VERTICAL : 'VERTICAL',
HORIZONTAL : 'HORIZONTAL',
}
_offCtx;
_offCanvas;
_scrollBar;
content;
bgColor;
barColor = '#fbe9b7'
scrollSide = ScrollView.ScrollSideType.VERTICAL;
scorllBarWidth;
scrollBarHeight;
itemArr = [];
constructor(ctx = null) {
super(ctx);
this._createOffCtx();
this._createScrollBar();
}
_createOffCtx() {
if (!this._offCtx) {
this._offCanvas = document.createElement('canvas'); // 创建一个新的canvas
this._offCanvas.width = this.width; // 创建一个正好包裹住一个粒子canvas
this._offCanvas.height = this.height;
this._offCtx = this._offCanvas.getContext('2d');
this.content = new MySprite(this._offCtx);
}
}
_createScrollBar() {
this._scrollBar = new ShapeRectNew();
this._scrollBar.anchorY = 0;
this._scrollBar.anchorX = 1;
this._scrollBar.setSize(10, 100, 5);
this._scrollBar.fillColor = this.barColor;
this.addChild(this._scrollBar);
}
setBgColor(color) {
this.bgColor = color;
}
setShowSize(w, h) {
this.width = w;
this.height = h;
if (this.content.width < this.width) {
this.content.width = this._offCanvas.width = this.width;
}
if (this.content.height < this.height) {
this.content.height = this._offCanvas.height = this.height;
}
this.refreshScrollBar();
}
setContentSize(w, h) {
this.content.width = w;
this.content.height = h;
this._offCanvas.width = w;
this._offCanvas.height = h;
}
addItem(sprite) {
this.itemArr.push(sprite);
this.content.addChild(sprite);
sprite.ctx = this._offCtx;
this.refreshContentSize();
}
refreshContentSize() {
const children = this.itemArr;
// console.log('children: ', children);
let maxW = 0;
let maxH = 0;
for (let i=0; i<children.length; i++) {
const box = children[i].getBoundingBox();
const curChild = children[i];
// console.log('box: ', box);
const boxEdgeX = curChild.x + (1 - curChild.anchorX) * curChild.width * curChild.scaleX;
const boxEdgeY = curChild.y + (1 - curChild.anchorY) * curChild.height * curChild.scaleY;
if (!children[i].colorRect) {
// const rect = new ShapeRectNew();
// rect.fillColor = '#ff0000';
// rect.setSize(curChild.width * curChild.scaleX, curChild.height * curChild.scaleY, 0);
// rect.alpha = 0.3;
// rect.x = boxEdgeX - curChild.width * curChild.scaleX;
// rect.y = boxEdgeY - curChild.height * curChild.scaleY;
// this.content.addChild(rect);
// children[i].colorRect = rect;
}
// console.log('boxEdgeY: ', boxEdgeY);
if (boxEdgeX > maxW) {
maxW = boxEdgeX;
}
if (boxEdgeY > maxH) {
maxH = boxEdgeY;
}
}
// console.log('maxW: ', maxW);s
// console.log('maxH: ', maxH);
// console.log('this.y: ', this.y);
// console.log(this.getBoundingBox().height);
// const box = this.getBoundingBox();
this.content.width = maxW;
this.content.height = maxH + 10 //+ 500;
this.refreshScrollBar();
}
setScrollBarSize(w, h) {
this.scorllBarWidth = w;
this.scrollBarHeight = h;
}
setContentScale(s) {
this.content.setScaleXY( 1 / s);
this.refreshScrollBar();
}
refreshScrollBar() {
let w = this.scorllBarWidth;
if (!w) {
w = this.width / 50;
}
const rect1 = this.getBoundingBox();
const rect2 = this.content.getBoundingBox();
const sRate = rect1.height / this.height;
let rate = rect1.height / rect2.height;
// let rate = this.height / this.content.height;
// let rate = this.height * this.scaleY / this.content.height / this.content.scaleY;
if (rate >= 1) {
this._scrollBar.visible = false;
rate = 1;
} else {
this._scrollBar.visible = true;
}
const h = rate * this.height / sRate;
const r = w / 2;
this._scrollBar.setSize(w, h, r);
this._scrollBar.x = this.width;
}
refreshScrollBarPos() {
const rect1 = this.getBoundingBox();
const rect2 = this.content.getBoundingBox();
let rate = this.height / this.content.height;
this._scrollBar.y = -this.content.y * (rate );
}
drawSelf() {
super.drawSelf();
this._offScreenRender();
}
touchStartPos;
touchStartContentPos;
onTouchStart(x, y) {
if (!this._scrollBar.visible) {
return;
}
this.touchStartPos = { x, y };
this.touchStartContentPos = { x: this.content.x, y: this.content.y };
}
onTouchMove(x, y) {
if (!this.touchStartPos) {
return;
}
if (!this.touchStartContentPos) {
return;
}
const offsetX = x - this.touchStartPos.x;
const offsetY = y - this.touchStartPos.y;
const rect = this.getBoundingBox();
const rect2 = this.content.getBoundingBox();
if (this.scrollSide == ScrollView.ScrollSideType.VERTICAL) {
this.content.y = between(this.touchStartContentPos.y + offsetY, 0, this.height - this.content.height);
} else {
this.content.x = between(this.touchStartContentPos.x + offsetX, 0, this.width - this.content.width);
}
this.refreshScrollBarPos();
}
onTouchEnd(x, y) {
this.touchStartPos = null;
this.touchStartContentPos = null;
}
onWheelUp(offsetY) {
if (!this._scrollBar.visible) {
return;
}
const rect = this.getBoundingBox();
if (this.scrollSide == ScrollView.ScrollSideType.VERTICAL) {
this.content.y = between(this.content.y + 40, 0, this.height - this.content.height);
} else {
this.content.x = between(this.content.x + 40, 0, this.width - this.content.width);
}
this.refreshScrollBarPos();
}
onWheelDown(offsetY) {
if (!this._scrollBar.visible) {
return;
}
const rect = this.getBoundingBox();
if (this.scrollSide == ScrollView.ScrollSideType.VERTICAL) {
this.content.y = between(this.content.y - 40, 0, this.height - this.content.height);
} else {
this.content.x = between(this.content.x - 40, 0, this.width - this.content.width);
}
this.refreshScrollBarPos();
}
setContentSpr() {
}
_offScreenRender() {
this._offCtx.save();
this._offCtx.clearRect(0, 0, this._offCanvas.width, this._offCanvas.height);
if (this.bgColor) {
this._offCtx.fillStyle = this.bgColor;
this._offCtx.fillRect(this._offX, this._offY, this.width, this.height);
this._offCtx.globalCompositeOperation = "source-atop";
} else {
this._offCtx.fillStyle = '#ffffff'
this._offCtx.fillRect(this._offX, this._offY, this.width, this.height);
this._offCtx.globalCompositeOperation = "xor";
}
this.content.update();
this.ctx.drawImage(this._offCanvas, this._offX, this._offY);
// this.ctx.drawImage(this._offCanvas, this._offX, this._offY);
// this.ctx.drawImage(this._offCanvas, this._offX, this._offY, this.content.width, this.content.height, this._offX + this.content.x, this._offY + this.content.y, this.width, this.height);
this._offCtx.restore();
}
}
export function between(a, b, c) {
return [a, b, c].sort((x, y) => x - y)[1];
}
// --------------- custom class --------------------
export async function loadFonts (fontName, fontFile) {
// const font = new FontFace(
// "DroidSansFallback",
// "url(" + "../../assets/play/font/DroidSansFallback.ttf" + ")"
// );
try {
console.log(location.href.split("#")[0] + "assets/play/font/" + fontFile);
const font = new FontFace(
fontName,
"url(" + location.href.split("#")[0] + "../../assets/play/font/" + fontFile + ")"
);
console.log('start: ', new Date().getTime())
await font.load();
console.log('end: ', new Date().getTime())
document.fonts.add(font);
document.body.classList.add("fonts-loaded");
} catch (error) {
console.log(error);
}
}
// --------------- custom class --------------------
import { data } from "./words";
export function checkAnswer(type, string, word) {
console.log('word: ', word);
switch (type) {
case 'device':
case 'side':
case 'work':
case 'adj':
return WordData.getInstance().check(type, string);
case 'name':
case 'city':
return checkCity(string);
case 'time':
return checkTime(string);
case 'date':
return checkDate(string);
case 'word':
return checkWord(string, word);
case 'wordList':
return checkWordInList(string, word);
default:
throw "错误的格式";
}
}
function checkCity(string) {
const bigCase = ('ABCDEFGHIJKLMNOPQRSTUVWXYZ').split('');
const smallCase = ('abcdefghijklmnopqrstuvwxyz').split('');
const right = string.split(' ')
.filter(word => word != '')
.every(word => {
return word.split('')
.every((letter, idx) => {
if (idx == 0) {
return bigCase.includes(letter)
&& !smallCase.includes(letter);
} else {
return !bigCase.includes(letter)
&& smallCase.includes(letter);
}
});
});
return {
right: right,
info: '专有名词首字母应该大写。',
}
}
function checkTime(string) {
const rightResult = {
right: true,
info: '',
}
const wrongResult = {
right: false,
info: 'time格式填写错误。正确格式为“时 分”, 例如:ten thirty',
};
if (string.includes(":")) {
const words = string.split(":");
if (words.length != 2) {
return wrongResult;
}
if (words.some(word => word.length > 2)) {
return wrongResult;
}
const hour = parseInt(words[0]);
const minute = parseInt(words[1]);
if (hour < 0 || 12 < hour) {
return wrongResult;
}
if (minute < 0 || 59 < minute) {
return wrongResult;
}
return rightResult;
}
const hours = [
"one", "two", "three", "four", "five", "six",
"seven", "eight", "nine", "ten", "eleven", "twelve"
];
const minutes = [
"o'clock", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten",
"eleven", "twelve", "thirteen", "fourteen", "fifteen", "a quarter", "sixteen", "seventeen", "eighteen", "nineteen", "twenty",
"twenty-one", "twenty-two", "twenty-three", "twenty-four", "twenty-five", "twenty-six", "twenty-seven", "twenty-eight", "twenty-nine", "thirty", "half",
"thirty-one", "thirty-two", "thirty-three", "thirty-four", "thirty-five", "thirty-six", "thirty-seven", "thirty-eight", "thirty-nine", "forty",
"forty-one", "forty-two", "forty-three", "forty-four", "forty-five", "three quarter", "forty-six", "forty-seven", "forty-eight", "forty-nine", "fifty",
"fifty-one", "fifty-two", "fifty-three", "fifty-four", "fifty-five", "fifty-six", "fifty-seven", "fifty-eight", "fifty-nine",
];
const middles = [
'to',
'past'
];
const middleWord = middles.find(word => string.includes(word));
if (middleWord) {
const strings = string.split(middleWord);
if (minutes.some(word => strings[0].trim().toLowerCase() == word)) {
if (hours.some(word => strings[1].trim().toLowerCase() == word)) {
return rightResult;
}
}
return wrongResult;
}
const strings = string.split(' ');
if (hours.some(word => strings[0].trim().toLowerCase() == word)) {
if (minutes.some(word => strings[1].trim().toLowerCase() == word)) {
return rightResult;
}
}
return wrongResult;
}
function checkDate(string: string) {
const wrongResult = {
right: false,
info: 'date格式填写错误。正确格式为“日 月 年”, 例如:15th August 2008',
};
const rightResult = {
right: true,
info: '',
}
let numberList = [];
if (string.includes('.')) {
numberList = string.split('.').map(string => parseInt(string));
} else {
const days1 = [
'none',
'First', 'second', 'third', 'fourth',
'fifth', 'sixth', 'seventh', 'eighth',
'ninth', 'tenth', 'eleventh', 'twelfth',
'thirteenth', 'fourteenth', 'fifteenth', 'sixteenth',
'seventeenth', 'eighteenth', 'nineteenth', 'Twentieth',
'twenty-first', 'twenty-second', 'Twenty-third', 'twenty-fourth',
'Twenty-fifth', 'Twenty-sixth', 'twenty-seventh', 'Twenty-eighth',
'twenty-ninth', 'Thirtieth', 'Thirty-first'
];
const days2 = [
'none',
'1st', '2nd', '3rd', '4th',
'5th', '6th', '7th', '8th',
'9th', '10th', '11th', '12th',
'13th', '14th', '15th', '16th',
'17th', '18th', '19th', '20th',
'21st', '22nd', '23rd', '24th',
'25th', '26th', '27th', '28th',
'29th', '30th', '31st'
]
const months = [
'none',
'January', 'February', 'March', 'April',
'May', 'June', 'July', 'August',
'September', 'October', 'November', 'December'
];
let yearFlg = false;
let monthFlg = false;
let dayFlg = false;
numberList = string.split(' ').map(string => {
if (!monthFlg) {
const month = months.findIndex(str => str == string);
if (month >= 1) {
monthFlg = true;
return month;
}
}
if (!dayFlg) {
let day = days1.findIndex(str => str == string);
if (day >= 1) {
dayFlg = true;
return day;
}
day = days2.findIndex(str => str == string);
if (day >= 1) {
dayFlg = true;
return day;
}
}
if (!yearFlg) {
const year: number = parseInt(string);
if (!isNaN(year)) {
yearFlg = true;
return year;
}
}
});
}
console.log('numberList = ' + JSON.stringify(numberList));
if (numberList.length < 2 || 3 < numberList.length) {
return wrongResult;
}
if (numberList.some(num => !num)) {
// 有null或者undefind
return wrongResult;
}
if (numberList.some(num => num <= 0)) {
// 负数
return wrongResult;
}
let rightFlg = true;
let yearFlg = false;
let monthFlg = false;
let dayFlg = false;
numberList.forEach((number, idx) => {
if (number > 31) {
if (!yearFlg) {
if (idx == 1) {
rightFlg = false;
} else {
yearFlg = true;
}
} else {
rightFlg = false;
}
} else if (number > 12) {
if (!dayFlg) {
dayFlg = true;
} else {
rightFlg = false;
}
} else {
if (!monthFlg) {
monthFlg = true;
} else if (!dayFlg) {
dayFlg = true;
} else {
rightFlg = false;
}
}
});
if (!monthFlg || !dayFlg) {
rightFlg = false;
}
if (rightFlg) {
return rightResult;
}
return wrongResult;
}
function checkWord(string: string, word: string) {
if (string.trim() != word.replace(/_/g, " ").trim()) {
return { right: false, info: word.replace(/_/g, " ").trim() };
}
return { right: true, info: '' };
}
function checkWordInList(string: any, word: any) {
const wordList: Array<string> = word;
if (wordList.includes(string)) {
return {
right: wordList.includes(string),
info: '错误的单词。'
};
} else {
return {
right: false,
info: '错误的单词。'
}
}
}
export class WordData {
static _instance = null;
static getInstance(): WordData {
if (!WordData._instance) {
WordData._instance = new WordData();
}
return WordData._instance;
}
constructor() {
this.device = data.device;
this.side = data.side;
this.work = data.work;
this.adj = data.adj;
}
device = ['TV', 'heater'];
side = ['north'];
work = ['teacher', 'worker'];
adj = ['big', 'small'];
check(type: any, string: any) {
const wrongString = {
device: '电器名错误。',
side: '方向名词错误。',
work: '工作名错误。',
adj: '形容词错误。',
}
return {
right: this[type].includes(string.trim()),
info: wrongString[type],
};
}
}
......@@ -3,6 +3,8 @@
height: 100%;
background: #ffffff;
background-size: cover;
}
#canvas {
......@@ -10,10 +12,28 @@
}
@font-face
{
font-family: 'BRLNSDB';
src: url("../../assets/font/BRLNSDB.TTF") ;
src: url("../../assets/play/font/BRLNSDB_1.TTF")
}
@font-face
{
font-family: 'Aileron-Black';
src: url("../../assets/play/font/Aileron-Black.ttf")
}
@font-face
{
font-family: 'Aileron-Bold';
src: url("../../assets/play/font/Aileron-Bold.ttf")
}
@font-face
{
font-family: 'DroidSansFallback';
src: url("../../assets/play/font/DroidSansFallback.ttf");
}
\ No newline at end of file
<div class="game-container" #wrap>
<canvas id="canvas" #canvas></canvas>
<span style="font-family:'DroidSansFallback'" ></span>
<span style="font-family:'Aileron-Black'" ></span>
<span style="font-family:'Aileron-Bold'" ></span>
<ng-template #tpl>
<video></video>
</ng-template>
<div style="display: none" #videoContainer></div>
<div class="game-container" #wrap >
<canvas id="canvas" style="width: 100%; height: 100%;" #canvas></canvas>
</div>
<div id='div_input'>
</div>
<!-- <div style="position: absolute; top: 0">
<label style="font-family: Aileron-Black;"></label>
<label style="font-family: DroidSansFallback;"></label>
</div> -->
This source diff could not be displayed because it is too large. You can view the blob instead.
const res = [
// ['bg', "assets/play/bg.jpg"],
['btn_left', "assets/play/btn_left.png"],
['btn_right', "assets/play/btn_right.png"],
// ['text_bg', "assets/play/text_bg.png"],
['op_item', "assets/play/op_item.png"],
['op_item_wrong', "assets/play/op_item_wrong.png"],
['op_item_right', "assets/play/op_item_right.png"],
['op_item_normal', "assets/play/op_item_normal.png"],
['btn_close', "assets/play/btn_close.png"],
['bg', "assets/play/bg.jpg"],
['video_bg', "assets/play/video_bg.jpg"],
['btn_replay', "assets/play/btn_replay.png"],
['icon_right', "assets/play/icon_right.png"],
['icon_wrong', "assets/play/icon_wrong.png"],
['answer_normal', "assets/play/answer_normal.png"],
['answer_right', "assets/play/answer_right.png"],
['answer_wrong', "assets/play/answer_wrong.png"],
];
......@@ -12,7 +24,14 @@ const res = [
const resAudio = [
['click', "assets/play/music/click.mp3"],
['submit', "assets/play/music/submit.mp3"],
['more', "assets/play/music/more.mp3"],
];
export {res, resAudio};
export const data = {
"device": ["vacuum cleaner", "electric fan", "air conditioner", "hair dryerstyler", "electric cooker", "desk lamp", "electric shaver", "micro wave oven", "frige", "washing machine", "electric heater", "fluorescent lamp", "dictaphone, dictating machine", "tape recorder", "television", "electric iron", "electric foot warmer", "electric vacuum cleaner", "bulb", "electronic oven", "radio", "loud-speaker", "refrigerator", "air conditioning", "microwave oven", "dry cell", "tap", "broiler", "defroster", "dicer", "dishwasher", "dryer", "eggbeater", "fan", "air-condition", "vertical disinfection cabinetffice", "horizontal disinfection cabinet", "pasteurize cupboard", "disinfectant tank", "household gas stove", "smoke exhauster", "air exhauster", "soya-bean milk grinder", "soya-bean milking", "food blender", "juice extractor", "filter purifier", "water dispenser", "can opener", "compactor", "freezer", "furnace", "humidifier", "iron", "juicer", "oven ", "percolator", "range hood", "rotisserie", "shaver", "stove", "toaster", "absorber", "ac accumulator", "ac azinuth comprator", "active loudspeaker", "acu automatic calling unit", "electric rice cooker", "electric kettle", "coffee maker", "blender", "toast oven", "exhaust fan", "disinfecting cabinet", "towel dispenser", "lcd tv", "electric fireplace", "wine cabinet", "cool freezer", "massager", "hair dryer", "electric toolbrush", "depilation equipment", "hair crimper", "compactordefroster", "lithium battery/cell", "lcd(liquid crystal displaytv", "electric fireplaceelectric fan", "electric toolbrushelectric iron", "tv", "computer", "drive", "monitor", "speaker", "air-conditioning", "flashlight", "calculator", "electric light", "electric calculator", "tube", "dictaphone,dictating machine", "electric shaverelectric cooker electric heaterelectric vacuum cleanerbulb", "microphone", "air conditioning microwave ovendry cell", "airconditioning", "electricfootwarmer", "electricshaver", "electriccooker", "electricheater", "electricvacuumcleaner", "electronicoven", "loudspeaker", "microwaveoven", "drycell", "boiler", "aircondition", "householdgasstove", "smokeexhauster", "airexhauster", "soyabeanmilkgrinder", "foodblender", "juiceextractor", "filterpurifier", "waterdispenser", "oven", "rangehood", "vacuumcleaner", "electriccalculator", "taperecorder", "projection lamp", "large screen splicing", "professional display", "industry display", "plasma panel display", "digital signage", "led display", "touch one machine", "touch screen", "recreational machine", "psp nds handheld game machine", "recreational machine ", "laser printer", "bubble jet printer", "multifunctional integrated machine", "large format printer", "scanner", "the fax machine", "copier", "copying machine", "dye sublimation printer", "projection booth", "projection screen", "electronic whiteboard", "automobile electric productsthe", "education electric products", "cd", "camera", "digital video (dv)", "camera lens", "mp3", "mp4", "u disk", "e-book", "electric book", "digital voice recorder", "earphone", "electric dictionary", "digital phone frameusb", "repeater", "point reading machine", "multimedia hard disk recoding", "portable source", "digital companion", "pick-up head", "desktop computer", "all-in-one desktops", "workstation", "jotter", "notebook", "netbooks", "iphone", "ipad", "panel computer", "computer components", "mouse", "liquid crystal display", "loudspeaker box", "voice box", "cell phone", "mobile phone", "phone", "gps", "walkie-talkie", "intercom", "telephone", "teleconference", "dial conference", "conference call", "do mobile phone", "electric toothbrush", "bath heater", "soap dispenser", "television set", "washer", "mosquito killer", "electric mosquito swatter", "electric mosquito repellent", "electro-thermal mosquito-repellent incense", "gas water heater", "electric water heater", "solar heater", "air source heat gong water heater", "water heater", "bracker fan", "desk fan", "floor fan", "ceiling fan", "wall fan", "air conditioning fan", "warm fan", "hand warmer", "heat insulation plate", "insulating pad", "insulation chests", "incubator", "hair removal device", "depilator", "shaving device", "electric eyebrow shaving device ", "electric eyelash curler", "beauty device", "crimping iron", "curler", "straighener", "pore cleaner", "nose hair trimmer", "electric manicure", "eyelash pen", "heating apparatus", "electric blanket", "foot bath", "ear cleaner", "hair cap", "electric massager", "barber", "the fat meter", "deaf-aid", "hemomanometer", "sphy gmomanometer", "pedometer", "bathroom scale", "weight balance", "weight scale", "glucometer", "moisture extractor", "humidity regulator", "perfuming machine", "perfuming device", "steam clean machine", "electronic trash", "air purifier", "air-cleaner", "water purifying machine", "vacuum cleaner,", "sweeping machine", "oxygen machine", "oxygen bar", "bread machine", "egg boiler", "steamed egg", "sandwich machine", "electric baking pan", "soybean milk machine", "ice cream machine", "coffee machine", "popcorn machine", "mill", "winepress,juicer", "fruit and vegetable cleaning machine", "clothes drier", "(electric) kettle", "electric cup", "fridge", "shoes dryer", "shoe polisher", "hand dryer", "electronic thermometer", "electronic timer", "reminder", "hair ball trim", "spin drier", "dehydrator", "agitator", "amalgamator", "cooking machine", "dish-washing machine", "food waste processor", "disinfection cabinet", "electric oven", "cheese furnace", "decoction vessel", "block shaving machine", "ice crusher", "fry ice machine", "hotplate,gas cooker", "casserole", "electromagnetic oven", "induction cooker", "electric (rice) cooker", "electric pressure cooker", "electric chafing dish", "the convection oven", "electric frying pan", "electric steamer", "the soup pot", "slow cooker ", "juicer ", "toaster ", "grill/barbeque ", "micro oven ", "toaster over", "coffer maker", "kettle", "ice cream maker", "break maker", "pasta maker", "mixer/blader", "food process", "deep fryer", "dishwasher ", "water pump", "compressed water pump", "Refrigerators", "freezers", "wine cabinets", "ice makers", "air conditioners", "washing machines", "dryers", "televisions", "home theaters", "projectors", "DVD players", "combination audio", "recorders", "tablets", "video game consoles", "Walkmans", "PDAs", "electronic dictionaries", "learning machines", "mobile memory", "digital cameras Electric paper books", "walkie-talkies", "car refrigerators", "mahjong machines", "fumes", "cooktops", "dishwashers", "disinfection cupboards", "gas water heaters", "electric hot water taps", "electric water heaters", "heat pump water heaters", "body cleaners", "hair dryers", "bathers", "dry mobile phones", "integrated ceiling", "soy milk machine", "rice cooker", "electric cake", "electric stew pot", "noodle machine", "yogurt machine", "fruit and vegetable detoxifier Egg-boiling machine", "egg-laying machine", "bean sprout machine", "oil press", "kitchen treasure", "garbage disposal machine", "solar water heater", "solar lawn lamp", "courtyard lamp", "solar toys", "solar panels", "0401 water treatment appliances: water dispensers", "water purifiers", "pure water machines", "soft water machines", "pipeline machines", "straight drinker", "defying vacuum cleaner", "steam mop", "sweeper", "floor waxing machine", "ironing machine", "shoe shine machine", "electric mosquito racket", "toilet deodorant", "cold fan", "dehumidifier", "negative oxygen ion occurrence devices", "small oxygen generators", "electric heaters", "electric blankets", "shavers", "curlers", "electric toothbrushes", "beauty devices", "electric breast pumps", "foot baths", "massage chairs", "massagers", "blood pressure meters", "thermometers", "body fat meters", "smart home systems", "Smart lighting control smart home central control system", "smart home ecological control", "smart shade", "smart door and window control", "smart socket", "security", "monitoring", "access control", "visual intercom", "set-top box", "smart switch", "information appliances", "home network system", "system integration product communication network and component sensor controller", "smart bracelet", "smart watch", "smart toys", "smart drop device", "router", "electrodrill", "electric drill", "electrical drill", "electron telescope", "electronic telescope"],
"side": [
"east",
"south",
"west",
"north",
"northeast",
"northwest",
"southeast",
"southwest",
"middle"
],
"work": ["Social workers", "personnelmanagers", "translators", "human mediators", "librarians", "journalists", "newspaper and magazine editors", "screenwriters", "book editors", "stone carvers", "carpenters", "cartoonists", "dancers", "instrument players", "construction and engineering management", "steel structure design and management personnel", "microcomputers", "electronic newspaper/e-magazine editors", "information managers", "kindergarten education teachers", "chain store managers", "marketing planning", "telecommunications engineers", "web designers", "Construction machinery operators", "electrical engineers", "mechanical cartographers", "architectural cartographers", "electronic processing data system operators", "photography staff", "merchant ship staff", "civil aviation transport pilots", "turbine personnel", "flight controllers", "dietitians", "eyewear professionals", "medical records managers", "insurance support personnel", "salesmen", "securities dealers", "sales agents", "real estate agents", "procurement personnel", "import and export clerks", "futures brokers", "accountants", "commercial art designers", "interior designers", "TV presenter", "advertising AE", "international trade practitioner", "international trade English clerk", "Chinese typist", "secretary of affairs", "information login", "assistant accountant", "quality control assistant", "warehouse manager", "production planning assistant", "distribution staff", "postman", "postal staff", "customs clearance Staff", "guest management", "bank clerks", "financial tellers", "foreign exchange traders", "travel agents", "tour guides", "flight attendants", "cultural relics narrator", "chef", "bartender", "Western food chef", "meal attendant", "nanny", "beauty barber", "beautician", "preservation Staff", "police", "fire workers", "fashion models", "merchandise salesmen", "pet beauticians", "horticultural crop growers", "nursery workers", "florists", "builders", "plumbers", "industrial plumbers", "industrial wiring operators", "indoor wiring workers", "electricians", "Sanitary plumbers", "magnetic brick pavers", "wiring workers", "painters", "automotive board goldworkers", "welders", "general board goldworkers", "special welders", "foundries", "metal mold makers", "fitters", "car repairmen", "automotive electricians", "Transaction machine repairer", "heavy mechanical repairman", "car chassis worker", "precision grinder", "milling machine worker", "numerical control lathe operator", "refrigeration air conditioner", "refrigeration air conditioner decorator", "audio-visual electronics worker", "industrial electronic worker", "bread baker", "motor repairer", "electrical repair Workers", "electronic instrument repairers", "jewelry and precious metals manufacturers", "precision gage manufacturers", "instrument tuners", "precision machinery", "ceramics workers", "printers", "printing design and plate makers", "bakers", "food and beverage technicians", "carpentry", "furniture carpentry", "Sewing", "weaver", "suit worker", "national clothing seamstress", "shoemaker", "garment design and production staff", "women's fitter", "garment worker", "heat treatment worker", "metal modeling", "metal surface treatment worker", "plate maker", "cold work", "planer", "metal plating Workers", "plastic mold manufacturers", "plastic products manufacturers", "rubber products manufacturers", "paper products manufacturers", "photo plate makers", "dairy manufacturers", "electronic assembly personnel", "automobile drivers", "fishing boat crews", "lathe workers", "oil and gas pressure automatic control personnel", "cleaning service personnel", "professional athletes teachers worker", "Teachers", "workers", "actors", "cooks", "doctors", "nurses", "drivers", "military personnel", "lawyers", "businessmen", "shop assistants", "cashiers", "writers", "models", "singers", "tailors", "judges", "security guards", "florists Waiter", "Cleaning workers", "architects", "hairdressers", "buyers", "designers", "firefighters", "mechanics", "magicians", "postmen", "lifeguards", "athletes", "engineers", "pilots", "administrators", "brokers", "auditors", "Cartoonist", "gardener", "scientist", "host", "make-up artist", "music festival", "artist", "pastry chef", "dessertr", "athlete", "diplomat", "dancer", "archery", "actor", "skater", "piano player", "guzheng hand", "designer", "bar owner", "CEO", "Amusement park owner", "captain", "journalist", "racer", "lawyer", "barber", "taekwondo coach", "veterinarian", "special police", "masseuse Java software engineer", "ERP software development engineer", "WAP development engineer", "software test engineer", "document engineer", "Internet software development engineer", "search engine engineer", "game development engineer", "web designer", "network engineer", "network administrator", "website editor", "embedded software development engineer", "electronic technology research and development engineer", "electronic engineer", "communication technology engineer", "mobile communication engineer", "3G software engineer", "mobile phone application development engineer", "urban planning designer", "civil engineer", "water supply and drainage engineer", "HVAC engineer", "engineering budgeter", "contract engineer", "property management specialist", "product process engineer", "Quality Management Specialist", "Supply Chain Specialist", "Automotive Electronics Engineer", "Automotive Technical Support Engineer", "Electrical Development Engineer", "Digital Product Development Engineer", "Mold Engineer", "Mechanical Engineer", "Pre-Sales Technical Engineer", "Aftermarket Technical Engineer", "Bioengineering Technician", "Chemical Technology Engineer", "Materials Engineer", "Water Treatment Engineer", "Strong Electrical Engineer", "Weak Electrical Engineer", "Electrical Engineer", "Automation Engineer", "Electrical and Mechanical Engineer", "Mining Engineer", "Geoengineer", "Physician", "Surgeon", "Pharmacist", "Pharmaceutical technology developer", "drug registrar", "bench worker", "drawing worker", "field worker", "forestry worker", "lathe worker", "lifting worker", "harbor worker", "iron worker", "Technical Worker", "accountant", "actress", "airlinerepresentative", "anchor", "announcer", "architect", "associateprofessor", "astronaut.", "attendant", "auditor", "automechanic", "baker", "baseballplayer", "bellboy", "bellhop", "binman", "blacksmith", "boxer", "broker(agent)", "budgeteer", "busdriver", "butcher", "buyer", "carpenter", "cashier", "chemist", "clerk", "clown", "cobbler", "computerprogrammer", "constructionworker", "cook", "cowboy", "customsofficer", "dentist", "deskclerk", "detective", "doctor", "door-to-doorsalesman", "driver", "dustman", "editor", "electrician", "engineer", "farmer", "fashiondesigner", "fireman(firefighter)", "fisherman", "florist", "flyer", "Foreignminister", "gasstationattendant", "geologist", "guard", "guide", "hiredresseer", "housekeeper", "housewife", "interpreter", "janitor", "judge", "librarian.", "lifeguard", "magician", "masseur", "masseuse", "mathematician", "mechanic", "miner", "model", "monk", "moviedirector", "moviestar", "musician", "nun", "nurse", "officeclerk", "officestaff", "operator", "parachutist.", "photographer", "pilot", "planner", "policeman", "postalclerk", "President", "priest", "processfor", "realestateagent", "receptionist", "repairman", "reporter", "sailor", "salesman/selespeople/salesperson", "seamstress", "secretary", "singer", "soldiery", "statistician", "surveyor", "tailor", "taxidriver", "teacher", "technician", "tourguide", "trafficwarden", "translator", "TVproducer", "typist", "vet", "waiter", "waitress", "welder", "writer", "MarketingandSales", "Vice-PresidentofSales", "SeniorCustomerManager", "SalesManager", "RegionalSalesManager", "MerchandisingManager", "SalesAssistant", "WholesaleBuyer", "Tele-Interviewer", "RealEstateAppraiser", "MarketingConsultant", "MarketingandSalesDirector", "MarketResearchAnalyst", "Manufacturer\\'sRepresentative", "DirectorofSubsidiaryRights", "SalesRepresentative", "AssistantCustomerExecutive", "MarketingIntern", "MarketingDirector", "InsuranceAgent", "CustomerManager", "Vice-PresidentofMarketing", "RegionalCustomerManager", "SalesAdministrator", "TelemarketingDirector", "AdvertisingManager", "TravelAgent", "Salesperson", "Telemarketer", "SalesExecutive", "MarketingAssistant", "RetailBuyer", "RealEstateManager", "RealEstateBroker", "PurchasingAgent", "ProductDeveloper", "MarketingManager", "AdvertisingCoordinator", "AdvertisingAssistant", "AdCopywriter(DirectMail)", "CustomerRepresentative", "ComputersandMathematics()", "ManagerofNetworkAdministration", "MISManager", "ProjectManager", "TechnicalEngineer", "DevelopmentalEngineer", "SystemsProgrammer", "Administrator", "OperationsAnalyst", "ComputerOperator", "ProductSupportManager", "ComputerOperationsSupervisor", "DirectorofInformationServices", "SystemsEngineer", "HardwareEngineer", "ApplicationsProgrammer", "InformationAnalyst", "LANSystemsAnalyst", "HumanResources", "DirectorofHumanResources", "AssistantPersonnelOfficer", "CompensationManager", "EmploymentConsultant", "FacilityManager", "JobPlacementOfficer", "LaborRelationsSpecialistRecruiter", "TrainingSpecialist", "Vice-PresidentofHumanResources", "AssistantVice-PresidentofHumanResources", "PersonnelManager", "BenefitsCoordinator", "EmployerRelationsRepresentative", "PersonnelConsultant", "TrainingCoordinator", "ExecutiveandManagerial", "ChiefExecutiveOfficer(CEO)", "DirectorofOperations", "Vice-President", "BranchManager", "RetailStoreManager", "HMOProductManager", "OperationsManager", "AssistantVice-President", "FieldAssuranceCoordinator", "ManagementConsultant", "DistrictManager", "HospitalAdministrator", "Import/ExportManager", "InsuranceClaimsController", "ProgramManager", "InsuranceCoordinator", "InventoryControlManager", "RegionalManager", "ChiefOperationsOfficer(COO)", "GeneralManager", "ExecutiveMarketingDirector", "Controller(International)", "FoodServiceManager", "ProductionManager", "PropertyManager", "ClaimsExaminer", "Controller(General)", "ServiceManager", "ManufacturingManager", "VendingManager", "TelecommunicationsManager", "TransportationManager", "WarehouseManager", "AssistantStoreManager", "Manager(Non-ProfitandCharities)", "simultaneous", "publisher", "graphicdesigner", "deliveryboy", "director", "talent", "producer", "scholar", "novelist", "playwright", "linguist", "botanist", "economist", "philosopher", "politician", "physicist", "astropologist", "archaeologist", "expertonfolklore", "biologist", "zoologist", "physiologist", "futurologist", "artists", "painter", "composer", "sculptor", "fashioncoordinator", "dressmaker", "cutter", "sewer", "ballerina", "firefigher", "chiefofpolice", "mailman", "newspaperboy", "bootblack", "poet", "copywriter", "newscaster", "milkman", "merchant", "greengrocer", "fish-monger", "shoe-maker", "saleswoman", "stewardess", "conductor", "stationagent", "porter", "carmechanic", "civilplanner", "civilengineer", "druggist", "oilsupplier", "(public)healthnurse", "supervisor", "forman", "keypuncher", "stenographer", "telephoneoperator", "programmer", "systemanalyst", "shorthandtypist", "officegirl", "publicservants", "nationalpublicservant", "localpublicserviceemployee", "nationrailroadman", "tracer", "illustrator", "acountant", "businessman", "tradesman", "pedlar", "meson", "spinner", "dyer", "temporaryworker", "probationer", "lecturer", "headmaster", "headmistress", "personnel", "administrative director", "file clerk", "executive assistant", "office manager", "executive secretary", "general office clerk", "inventory control analyst", "staff assistant", "mail room supervisor", "order entry clerk", "telephone operator", "shipping/receiving expediter", "ticket agent", "vice-president of administration", "daycare worker", "esl teacher", "developmental educator '", "head teacher", "foreign language teacher", "librarian", "guidance counselor", "music teacher", "library technician", "physical education teacher", "principal", "school psychologist", "special needs educator", "teacher aide", "art instructor", "computer teacher", "college professor", "coach", "assistant dean of students", "archivist", "vocational counselor", "tutor", "police officer", "fire fighter", "police sergeant", "assistant attorney general", "law clerk", "contracts manager", "law student", "ombudsman", "paralegal", "security manager", "patent agent", "legal assistant", "court officer", "legal secretary", "court reporter", "attorney", "clinical director", "cardiologist", "dental hygienist", "chiropractor", "dental technician", "dental assistant", "dietary technician", "emergency medical technician", "dietician", "fitness instructor", "home health aide", "health services coordinator", "lab technician", "hospital supervisor", "medical records clerk", "nursing aide", "medical technologist", "nursing supervisor", "nursing administrator", "nutritionist", "nursing home manager", "optician", "occupational therapist", "orthodontist", "veterinary assistant", "pharmacy technician", "psychiatrist", "physical therapist", "physician's assistant", "respiratory therapist", "pediatrician", "speech pathologist", "sanitation inspector", "wait person", "customer service manager", "customer service representative", "caterer", "fast food worker", "health club manager", "cosmetologist", "hotel concierge", "hotel manager'", "food inspector", "hotel clerk", "restaurant manager", "hairstylist", "flight attendant", "technical", "technical llustrator", "landscape architect", "research and development technician", "aircraft mechanic", "quality control inspector", "qatest technician", "precision inspector", "drafter", "technical support specialist", "building inspector", "engineering technician", "electronic equipment repairer", "aircraft pilot", "deputy gencral managc", "economic rescarch assistan", "electrical enginccr", "enginccring technician", "english instructor/tcachcr", "export salcs managc", "export salcs staf", "financial controllcrfinancial reportcr", "f.x. (foreign exchangc)clcrk", "f.x. settlcmcnt clcrk", "fund managcr", "gcncral auditor", "gcncral managcr", "gcncral managcr assistant", "gcncral managcr's sccrctary", "hardwarc enginccr )", "accounting assistant", "accounting clcrk", "accounting managcraccounting stall ", "accounting supervisor", "administration managcr", "administration staff", "administrativc assistant ", "administrativc clerk ", "advcrtising staff ", "airlincs sales rcprcscntativc", "airlincs staff ", "application enginccr", "assistant managcr", "bond analyst ", "bond trader", "business controllcr", "business managcr", "buycr ", "cashicr ", "chcmical enginccr", "civil enginccr", "clcrk receptionist", "clerk typist & secrctary", "computcr data input operator", "computcr enginccr", "computcr proccssing operator", "computer systcm managcr", "deputy gcncral managc", "gencral managcrl prcsident", "gcneral managcr assistant.", "gcneral managcr's secrctary", "hardwarc enginecr)", "lmport liaison staff", "import managcr", "insurancc actuary", "intcrnational salcs staff", "intcrprctcr ", "lcgal adviscr", "linc supcrvisor", "maintenancc enginccr", "managcmcnt consultant ", "managcr", "managcr for public rclation", "manufacturing enginccr", "manufacturing workcr", "markct analyst", "markct devclopmcnt managcr", "markcting managcr", "markcting staff", "markcting assistant", "markcting exccutivc", "markcting rcprcscntativc", "markcting rcprcscntativc managcr", "mechanical enginccr", "mining enginccr", "naval architect", "officc assistant", "officc clerk ", "opcrational managcr", "packagc designcr", "passenger rcservation staff", "pcrsonnel clcrk ", "pcrsonncl managcr", "plant/ factory managcr", "postal clerk", "privatc secrctary", "product managcr", "production enginecr", "profcssional staff", "project staff ", "promotional managcr", "proof-rcader", "purchasing agcnt ", "quality control enginccr", "rcal estatc staff", "rccruitment co-ordinator", "rcgional mangcr", "rcscarch&.devclopment enginccr", "rcstaurant managcr", "salcs and planning staff", "salcs assistant ", "salcs clerk ", "salcs coordinator", "salcs enginccr", "salcs exccutivc", "salcs managcr", "scller rprcscntativc", "salcs supcrvisor", "school rcgistrar", "secrctarial assistant", "securitics custody clcrk", "security officcr", "scnior accountant", "senior consultant/adviscr", "scnior employcc", "senior sccrctary", "scrvicc managcr", "simultancous interprctcr", "softwarc enginccr", "systcms adviscr", "systems enginccr", "systems operator ", "tcchnical editor", "technical translator", "tcchnical workcr", "tclecommunication executivc()", "tclcphonist / opcrator", "tourist guidc", "tradc financc exccutivc", "trainec managcr", "translation checkcr", "'translator", "trust banking exccutivc", "wordproccssor operator", "actrcss", "airlinc reprcscntativc ", "announccr", "architcct", "associatc profcssor", "astronaut", "attcndant", "auto mechanic ", "bakcr", "barbcr", "bascball playcr", "bcll boy", "binman,", "boxcr", "broker (agcnt) ", "budgctecr", "bus driver", "taxi driver", "subway driver", "train driver", "butchcr,", "buycr", "carpentcr", "cashicr", "chcmist ", "clerk ", "clown ", "cobblcr", "computcr programmcr ", "construction workcr ", "cowboy ", "customs officcr", "danccr ", "designcr", "dcsk clcrk", "detcctivc", "door-to-door salesmen"],
"adj": [
"big", "small", "red", "blue", "good", "bad"
]
}
\ No newline at end of file
// 刷新CDN路径
// https://staging-teach.cdn.ireadabc.com/h5template/h5-static-lib/js/air.js
// https://teach.cdn.ireadabc.com/h5template/h5-static-lib/js/air.js
const commonPostMessage = function (messageObj) {
const obj = {...messageObj, urlParams: window.location.search}
window.parent.postMessage(obj, '*');
};
const commonPostMessageWithCallback = function (messageObj, callback) {
const onMessage = (e) => {
let msgData = e.data;
if(msgData && msgData.action === messageObj.action){
window.removeEventListener("message", onMessage);
callback && callback(msgData.data);
}
};
window.addEventListener("message", onMessage);
commonPostMessage(messageObj);
};
const realAir = {
uploadUrl: null,
uploadData: null,
getDataCallback: null,
setDataCallback: null,
getUploadCallback: null,
pageLoaded: false,
isCourseInScreen: false,
hideAirClassLoading: function(templateName,loadData){
// 隐藏页面加载时候的loading
if (deleteHistory && typeof (deleteHistory) == 'function') {
deleteHistory();
}
window.parent.postMessage({ action: "course-ready", data: {template:templateName,loadData:loadData,urlParams: window.location.search}, urlParams: window.location.search}, '*');
window.air.pageLoaded = true;
window.courseware.next();
var cc = window.cc;
if (cc && cc.director && cc.director._scene) {
try {
const canvas = cc.find("Canvas");
canvas.on("mousemove", function () {});
cc.director._scene.on("mousemove", function () {
window.parent.postMessage({ action: "mousemove" }, "*");
});
cc.director._scene.on("touchmove", function () {
window.parent.postMessage({ action: "mousemove" }, "*");
});
if (cc.systemEvent && cc.SystemEvent) {
cc.systemEvent.on(cc.SystemEvent.EventType.KEY_DOWN, (event) => {
switch (event.keyCode) {
case cc.KEY.left:
window.parent.postMessage({ action: "ArrowLeft" }, "*");
break;
case cc.KEY.right:
window.parent.postMessage({ action: "ArrowRight" }, "*");
break;
}
});
}
} catch (e) {
console.log("====cc.director._scene绑定事件====");
}
}
},
};
const uploadCallbackQueue = [];
try{
window.air = new Proxy(realAir, {
set: function (target, key, value, receiver) {
if (key=="getUploadCallback") {
uploadCallbackQueue.push(value);
}
return Reflect.set(target, key, value, receiver);
},
get: function (target, key, receiver) {
return Reflect.get(target, key, receiver);
},
deleteProperty: function(target, key){
return Reflect.deleteProperty(target, key);
}
});
}catch(e){
console.error("浏览器不支持ES6新特性Proxy/Reflect,请使用谷歌浏览器!");
}
function deleteHistory() {
const disableBack = () => {
window.history.pushState(null, "", document.URL);
window.addEventListener("popstate", () => {
window.history.pushState(null, "", document.URL);
});
}
disableBack();
window.addEventListener("load", disableBack);
}
deleteHistory();
if (window.self !== window.top) {
window.addEventListener("message", function (e) {
let msgData = e.data;
// console.log("子页面接收到了消息", msgData);
if(msgData.type === "webpackWarnings" || msgData.type === "webpackOk") {
return;
}
if(msgData.action==="airEvents"){
return;
}
if (msgData.action === "getUpload" && msgData.uploadUrl) {
window.air.uploadUrl = msgData.uploadUrl;
window.air.uploadData = msgData.uploadData;
for (let i = 0; i < uploadCallbackQueue.length; i++) {
uploadCallbackQueue[i](msgData.uploadUrl, msgData.uploadData);
}
return;
}
if (msgData.action === "pauseCocos") {
let cc = window.cc;
if (cc && cc.game) {
cc.game.pause();
console.log('pause了');
}
return;
}
if (msgData.action === "resumeCocos") {
let cc = window.cc;
if (cc && cc.game) {
cc.game.resume();
console.log('resume了');
}
return;
}
if (msgData.action === "restartCocos") {
let cc = window.cc;
if (cc && cc.game) {
cc.audioEngine.stopAll();
// cc.game.restart();
console.log('restart了');
}
return;
}
if (msgData.action === "setData") {
window.air.setDataCallback && window.air.setDataCallback();
return;
}
if (msgData.action === "getData") {
try {
const res = JSON.parse(msgData.data);
if (res.msg !== "success") {
console.log('数据加载失败!');
return;
}
if (res.data && res.data != 'null') {
window.air.callData = JSON.parse(res.data);
}
window.air.callDataFlag = true;
return;
} catch (e) {
console.log('数据加载失败!');
}
}
});
window.parent.postMessage({ action: "getUpload" }, '*');
document.onmousemove = function(){
window.parent.postMessage({ action: "mousemove" }, '*');
};
document.ontouchmove = function(){
window.parent.postMessage({ action: "mousemove" }, '*');
};
document.onkeydown = (event) => {
switch(event.key) {
case "ArrowLeft":
window.parent.postMessage({ action: "ArrowLeft" }, '*');
break;
case "ArrowRight":
window.parent.postMessage({ action: "ArrowRight" }, '*');
break;
}
}
document.oncontextmenu = function(){
return false;
}
}
window.courseware = (function () {
let obj = {
airEvents: {},
eventQueue: [],
eventLock: false,
next: () => {
let exe = obj.eventQueue.splice(0,1);
if(exe.length>0){
obj.eventLock = true;
let evtName = exe[0].evtName;
let data = exe[0].data;
if(obj.airEvents[evtName]){
if(evtName === "course-in-screen"){
if(window.air.isCourseInScreen){
return;
}
window.air.isCourseInScreen = true;
}
console.log(`evtName==${evtName}的方法被执行`);
obj.airEvents[evtName](data, obj.next);
} else {
console.warn(`airclass教室交互事件 ${evtName} 没有被捕获,请确认监听事件的先后顺序2`);
obj.next();
}
}else{
obj.eventLock = false;
}
}
};
if (window.self !== window.top) {
window.addEventListener("message", function (e) {
let msgData = e.data;
if(msgData.action!=="airEvents"){
return;
}
let evtName = msgData.evt;
let res = msgData.data;
if (evtName === "course-out-screen"){
console.log(`evtName==${evtName}的方法被执行`);
window.location.reload();
return;
}
if (res&&evtName!='userchange') {
//userchange事件传过来的值不需要转换
res = JSON.parse(res);
}
if(!window.air.pageLoaded){
//如果页面还没有加载完成
obj.eventQueue.push({"evtName": evtName, data: res});
}else{
if (obj.eventQueue.length === 0 && !obj.eventLock) {
//如果没有消息积压并且事件锁未锁定
obj.eventLock = true;
if(obj.airEvents[evtName]){
if(evtName === "course-in-screen"){
if(window.air.isCourseInScreen){
return;
}
window.air.isCourseInScreen = true;
}
console.log(`evtName==${evtName}的方法被执行`);
obj.airEvents[evtName](res, obj.next);
} else {
console.warn(`airclass教室交互事件 ${evtName} 没有被捕获,请确认监听事件的先后顺序1`);
obj.next();
}
} else {
obj.eventQueue.push({"evtName": evtName, data: res});
}
}
});
obj.getData = function (callback, key = '') {
window.parent.postMessage({ action: "getData", data: window.location.search, urlParams: window.location.search }, '*');
window.air.callDataFlag = false;
const liuintval = setInterval(()=>{
if(window.air.callDataFlag){
console.log("========我进来了=========");
clearInterval(liuintval);
setTimeout(() => {
console.log("执行回调,回调数据为:");
console.log(window.air.callData);
callback(window.air.callData);
}, 100);
}
}, 100);
};
obj.setData = function (data, callback, key = '') {
let str = JSON.stringify(data);
console.log("==setData==", str);
window.parent.postMessage({ action: "setData", data: str, urlParams: window.location.search }, '*');
window.air.setDataCallback = callback;
};
obj.uploadUrl = function () {
// return net.getUploadFileURL();
return window.air.uploadUrl;
};
obj.uploadData = function () {
// return net.getAjaxData("uploadFile","");
return window.air.uploadData;
};
obj.beganRecording = function(){
commonPostMessage({ action: "beganRecording" });
};
obj.endRecording = function(callback){
commonPostMessageWithCallback({ action: "endRecording" }, callback);
};
obj.speakPoints = function(audioUrl, evalText, callback){
const obj = {audioUrl, evalText};
commonPostMessageWithCallback({ action: "speakPoints", data: JSON.stringify(obj) }, callback);
};
obj.startRecord = function(testText){
commonPostMessage({ action: "startRecord", data: testText });
};
obj.stopRecord = function(callback){
commonPostMessageWithCallback({ action: "stopRecord" }, callback);
};
obj.startTest = function(testText){
commonPostMessage({ action: "startTest", data: testText });
};
obj.stopTest = function(callback){
commonPostMessageWithCallback({ action: "stopTest" }, callback);
};
obj.getTemplates = function(callback){
commonPostMessageWithCallback({ action: "getTemplates" }, callback);
};
obj.getTemplateUrl = function(templateName, callback){
commonPostMessageWithCallback({ action: "getTemplateUrl", data: templateName}, callback);
};
obj.sendEvent = function(evtName,data,key = ''){
if(evtName==="reconnect"){
console.error("reconnect是内置的断线重连事件名称,请勿使用此名称作为事件名");
return;
}
if(evtName==="userchange"){
console.error("userchange是内置的用户变更事件名称,请勿使用此名称作为事件名");
return;
}
let str = null;
if(data){
str = JSON.stringify(data);
}
window.parent.postMessage({ action: "airEvents",evt: evtName, data: str, urlParams: window.location.search }, '*');
};
obj.onEvent = function(evtName,callback){
obj.airEvents[evtName] = callback;
};
obj.removeEvent = function(evtName){
if(obj.airEvents[evtName]){
delete obj.airEvents[evtName];
}
};
obj.sendErrorLog = function (error) {
// todo 这块预留后面采集模板的报错信息
// window.parent.postMessage({ action: "errorlog", data: error.stack }, '*');
};
} else {
obj.getData = function (callback, key = '') {
let data = localStorage.getItem("courseware_data_" + key);
if (data) {
data = JSON.parse(data);
}
callback && callback(data);
};
obj.setData = function (data, callback, key = '') {
console.log("******local********");
localStorage.setItem("courseware_data_" + key, JSON.stringify(data));
callback && callback();
};
obj.uploadUrl = function () {
var protocolStr = document.location.protocol;
return `${protocolStr}//staging-teach.ireadabc.com/fileUpload`;
};
obj.uploadData = function () {
return {};
};
obj.beganRecording = function(){
console.log("******beganRecording********");
};
obj.endRecording = function(callback){
console.log("******endRecording********");
callback&&callback("");
};
obj.speakPoints = function(audioUrl, evalText, callback){
console.log("******speakPoints********");
callback&&callback("");
};
obj.startRecord = function(){
console.log("******startRecord********");
};
obj.stopRecord = function(callback){
console.log("******stopRecord********");
callback&&callback("");
};
obj.startTest = function(testText){
console.log("******startTest********");
};
obj.stopTest = function(callback){
console.log("******stopTest********");
callback&&callback("");
};
obj.getTemplates = function(callback){
callback&&callback("");
};
obj.getTemplateUrl = function(templateName, callback){
const obj = {
play_url: "",
form_url: "",
};
callback&&callback(JSON.stringify(obj));
};
obj.sendEvent = function(evtName, data, key = ''){
if(evtName==="reconnect"){
console.error("reconnect是内置的断线重连事件名称,请勿使用此名称作为事件名");
return;
}
if(evtName==="userchange"){
console.error("userchange是内置的用户变更事件名称,请勿使用此名称作为事件名");
return;
}
return;
};
obj.onEvent = function(evtName,callback){
console.warn("==本地测试用,onEvent的监听事件会直接触发==", "事件名:"+ evtName);
callback && callback("", function(){});
};
obj.removeEvent = function(evtName){
};
obj.sendErrorLog = function (error) {
};
}
return obj;
})();
src/assets/play/bg.jpg

25.8 KB | W: | H:

src/assets/play/bg.jpg

159 KB | W: | H:

src/assets/play/bg.jpg
src/assets/play/bg.jpg
src/assets/play/bg.jpg
src/assets/play/bg.jpg
  • 2-up
  • Swipe
  • Onion skin
......@@ -9,6 +9,12 @@
<link rel="icon" type="image/x-icon" href="favicon.ico">
<script type="text/javascript" src="https://staging-teach.cdn.ireadabc.com/h5template/h5-static-lib/js/air.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/vConsole/3.9.0/vconsole.min.js"></script>
<script>
// init vConsole
// var vConsole = new VConsole();
// console.log('Hello world');
</script>
</head>
<body>
<app-root></app-root>
......
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