Commit 25544071 authored by Chen Jiping's avatar Chen Jiping

feat:完成部分模板开发

parent 9f3a2900
......@@ -128,5 +128,8 @@
}
}
},
"defaultProject": "ng-template-generator"
}
"defaultProject": "ng-template-generator",
"cli": {
"analytics": "c98da0cb-87a0-4cb4-8a5d-5f3f3012ad61"
}
}
\ No newline at end of file
export class Obj{
/**音频材料 */
audioUrl: String;
/**图片材料 */
picUrl:String;
val : String;
}
\ No newline at end of file
import { Obj } from "./ObjBean";
export class SentenceBean extends Obj{
renderTo = "";
letterArr = [];
}
export function getDefaultSentence(){
let sentence = new SentenceBean();
return sentence;
}
export class LetterBean extends Obj{
fontColor = "#000000";
}
export function getDefaultLetter(){
let letter = new LetterBean();
return letter;
}
\ No newline at end of file
import { Obj } from "./ObjBean";
export class TitleBean extends Obj{
part1 : String;
part2: String;
}
export function getDefaultTile(){
let title = new TitleBean();
title.part1 = "C";
title.part2 = "Read and point";
return title;
}
\ No newline at end of file
......@@ -1959,6 +1959,8 @@ export class HotZoneItem extends MySprite {
private _itemType;
private shapeRect: ShapeRect;
key;
get itemType() {
return this._itemType;
}
......
<div class="p-image-children-editor">
<h5 style="margin-left: 2.5%;"> preview: </h5>
<h5 style="margin-left: 2.5%;">右侧图片</h5>
<div class="preview-box" #wrap>
<canvas id="canvas" #canvas></canvas>
</div>
<div nz-row nzType="flex" nzAlign="middle">
<div nz-col nzSpan="5" nzOffset="1">
<div nz-col nzSpan="3" nzOffset="1">
<h5> add background: </h5>
<h5>请上传一张图片</h5>
<div class="bg-box">
<app-upload-image-with-preview
......@@ -24,8 +24,7 @@
<div
style="margin: auto; padding: 5px; margin-top: 30px; width:90%; border: 2px dashed #ddd; border-radius: 10px">
<span style="margin-left: 40%;"> item-{{i + 1}}
</span>
<span>序号区域-{{i + 1}}</span>
<button style="float: right;" nz-button nzType="danger" nzSize="small" (click)="deleteBtnClick(i)">
X
</button>
......@@ -34,29 +33,7 @@
<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 *ngIf="it.itemType == 'pic'">
<app-upload-image-with-preview
[picUrl]="it?.pic_url"
(imageUploaded)="onItemImgUploadSuccess($event, it)">
</app-upload-image-with-preview>
</div>
<div *ngIf="it.itemType == 'text'">
<input type="text" nz-input [(ngModel)]="it.text" (blur)="saveText(it)">
</div>
<div style="width: 100%; margin-top: 5px;">
<app-audio-recorder
[audioUrl]="it.audio_url"
(audioUploaded)="onItemAudioUploadSuccess($event, it)"
></app-audio-recorder>
<input type="text" nz-input [(ngModel)]="it.text" (ngModelChange)="changeText(it)">
</div>
</div>
......@@ -64,27 +41,24 @@
</div>
<div nz-col nzSpan="5" nzOffset="1">
<div nz-col nzSpan="3" nzOffset="1">
<div class="bg-box">
<button nz-button nzType="dashed" (click)="addBtnClick()"
class="add-btn">
<i nz-icon nzType="plus-circle" nzTheme="outline"></i>
<!--Add Image-->
Add hot zone
添加序号
</button>
</div>
</div>
</div>
<nz-divider></nz-divider>
<div class="save-box">
<button class="save-btn" nz-button nzType="primary" [nzSize]="'large'" nzShape="round"
(click)="saveClick()" >
(click)="saveClick()" [disabled]="saveDisabled ? true : false">
<i nz-icon nzType="save"></i>
Save
</button>
......
......@@ -12,8 +12,8 @@
.preview-box {
margin: auto;
width: 95%;
height: 35vw;
width: 371px;
height: 539px;
border: 2px dashed #ddd;
border-radius: 0.5rem;
......
......@@ -57,11 +57,11 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
saveDisabled = true;
canvasWidth = 1280;
canvasHeight = 720;
canvasBaseW = 1280;
canvasWidth = 371;
canvasHeight = 539;
canvasBaseW = 371;
// @HostListener('window:resize', ['$event'])
canvasBaseH = 720;
canvasBaseH = 539;
mapScale = 1;
ctx;
mx;
......@@ -128,18 +128,24 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
onBackgroundUploadSuccess(e) {
console.log('e: ', e);
//console.log('e: ', e);
this.bgItem.url = e.url;
this.refreshBackground();
this.saveDisabled = false;
}
onItemImgUploadSuccess(e, item) {
item.pic_url = e.url;
this.loadHotZonePic(item.pic, e.url);
this.saveDisabled = false;
}
onItemAudioUploadSuccess(e, item) {
item.audio_url = e.url;
this.saveDisabled = false;
}
......@@ -152,7 +158,7 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
const bg = this.bg;
if (this.bgItem.url) {
bg.load(this.bgItem.url).then(() => {
const rate1 = this.canvasWidth / bg.width;
const rate2 = this.canvasHeight / bg.height;
......@@ -178,27 +184,35 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
// this.hotZoneArr.push({});
const item = this.getHotZoneItem();
item.key = 'key_' + new Date().getTime();
this.hotZoneArr.push(item);
this.hotZoneArr = [...this.hotZoneArr];
this.refreshItem(item);
this.refreshHotZoneId();
this.saveDisabled = false;
}
deleteBtnClick(index) {
const item = this.hotZoneArr.splice(index, 1)[0];
removeItemFromArr(this.renderArr, item.pic);
removeItemFromArr(this.renderArr, item.textLabel);
this.hotZoneArr = [...this.hotZoneArr];
this.refreshHotZoneId();
this.saveDisabled = false;
}
onImgUploadSuccessByImg(e, img) {
img.pic_url = e.url;
this.refreshImage(img);
this.saveDisabled = false;
}
refreshImage(img) {
......@@ -217,8 +231,8 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
this.hotZoneArr[i].index = i;
if (this.hotZoneArr[i]) {
this.hotZoneArr[i].title = 'item-' + (i + 1);
this.hotZoneArr[i].title = this.hotZoneArr[i].text;
this.hotZoneArr[i].showLineDash();
}
}
}
......@@ -238,8 +252,8 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
getHotZoneItem(saveData = null) {
const itemW = 200;
const itemH = 200;
const itemW = 50;
const itemH = 50;
const item = new HotZoneItem(this.ctx);
item.setSize(itemW, itemH);
item.anchorX = 0.5;
......@@ -257,7 +271,7 @@ 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.text = saveData.text;
}
item.showLineDash();
......@@ -337,14 +351,22 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
onAudioUploadSuccessByImg(e, img) {
img.audio_url = e.url;
this.saveDisabled = false;
}
onAudioUploadSuccess(e, item, key) {
item[key] = e.url;
this.saveDisabled = false;
}
deleteItem(e, i) {
// this.imgArr.splice(i , 1);
// this.refreshImageId();
this.hotZoneArr.splice(i, 1);
this.refreshHotZoneId();
this.saveDisabled = false;
}
......@@ -353,7 +375,7 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
this.refreshItem(item);
// console.log(' in radioChange e: ', e);
this.saveDisabled = false;
}
refreshItem(item) {
......@@ -449,9 +471,10 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
item.pic_url = data.pic_url;
item.text = data.text;
item.itemType = data.itemType;
item.key = data.key;
this.refreshItem(item);
console.log('item: ', item);
this.hotZoneArr.push(item);
}
......@@ -582,7 +605,7 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
this.oldPos = {x: this.mx, y: this.my};
this.saveDisabled = true;
this.saveDisabled = false;
}
......@@ -880,7 +903,8 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
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
mapScale: this.mapScale,
key:hotZoneArr[i].key
};
hotZoneItem['rect'] = hotZoneArr[i].getBoundingBox();
hotZoneItem['rect'].x = Math.round((hotZoneItem['rect'].x - bgItem['rect'].x) * 100) / 100;
......@@ -892,9 +916,11 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
hotZoneItemArr.push(hotZoneItem);
}
console.log('hotZoneItemArr: ', hotZoneItemArr);
//console.log('hotZoneItemArr: ', hotZoneItemArr);
this.save.emit({bgItem, hotZoneItemArr});
this.saveDisabled = true;
}
private updatePos() {
......@@ -983,4 +1009,14 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
pic.setScaleXY(s);
});
}
changeText(hotZoneItem = null){
if(hotZoneItem){
hotZoneItem.title = hotZoneItem.text;
hotZoneItem.showLineDash();
}
this.saveDisabled = false;
}
}
<div class="model-content">
<div style="position: absolute; left: 200px; top: 100px; width: 800px;">
<input type="text" nz-input [(ngModel)]="item.text" (blur)="save()">
<app-upload-image-with-preview
[picUrl]="item.pic_url"
(imageUploaded)="onImageUploadSuccess($event, 'pic_url')"
></app-upload-image-with-preview>
<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 nz-row>
<div nz-col nzOffset='4'>
<h1 nz-title>课程练习内容编辑</h1>
</div>
</div>
<div nz-row>
<div nz-col [nzSpan]="15" nzOffset="3">
<div nz-form>
<div id='title-anchor'>
<nz-divider nzText="课程名称" nzOrientation="left"></nz-divider>
<nz-form-item>
<nz-form-label [nzSpan]="3" nzFor="part1">标题1</nz-form-label>
<nz-form-control [nzSpan]="6">
<nz-input-group nzAddOnBefore="Part">
<input type="text" id="part1" nz-input [(ngModel)]="item.title.part1" (blur)="save()">
</nz-input-group>
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label [nzSpan]="3" nzFor="part2">标题2</nz-form-label>
<nz-form-control [nzSpan]="6">
<input nz-input type="text" id="part2" [(ngModel)]="item.title.part2" (blur)="save()">
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label [nzSpan]="3" nzFor="audioUrl">发音</nz-form-label>
<nz-form-control [nzSpan]="6">
<app-audio-recorder [audioUrl]="item.title.audioUrl" id="audioUrl"
(audioUploaded)="onAudioUploadSuccess($event, item.title, 'audioUrl')">
</app-audio-recorder>
</nz-form-control>
</nz-form-item>
</div>
<div id='listen-anchor'>
<nz-divider nzText="听力材料" nzOrientation="left"></nz-divider>
<nz-form-item>
<nz-form-control [nzSpan]="6" nzOffset="3">
<app-audio-recorder id="item.listenAudioUrl" [audioUrl]="item.listenAudioUrl"
(audioUploaded)="onAudioUploadSuccess($event, item, 'listenAudioUrl')">
</app-audio-recorder>
</nz-form-control>
</nz-form-item>
</div>
</div>
</div>
</div>
<br>
<br>
<div nz-row>
<div nz-col [nzSpan]="15" nzOffset="2">
<app-custom-hot-zone [bgItem]="item.bgItem" [hotZoneItemArr]="item.hotZoneItemArr" [defaultItemType]="'rect'"
(save)="saveData($event, item)"></app-custom-hot-zone>
</div>
</div>
<br>
<div nz-row>
<div nz-col [nzSpan]="24">
<nz-card [nzExtra]="actionAdd" [nzActions]="[actionSetting]" nzTitle="句子">
<nz-form-item>
<nz-form-label [nzSpan]="3" nzFor="titleVal">标题</nz-form-label>
<nz-form-control [nzSpan]="6">
<input nz-input type="text" id="titleVal" [(ngModel)]="item.titleVal" (blur)="save()">
</nz-form-control>
</nz-form-item>
<div *ngFor="let data of item.sentenceArr; let i=index" style="padding: 0.5vw;" nz-col [nzXs]='24' [nzSm]='20'
[nzMd]='20' [nzLg]='20'>
<nz-card nzTitle="句子-{{i+1}}" [nzHoverable]="true" [nzExtra]="actionDelSentence">
<nz-form-item>
<nz-form-label [nzSpan]="3">所属序号</nz-form-label>
<nz-form-control [nzSpan]="3">
<div *ngFor="let hotZoneItem of this.item.hotZoneItemArr">
<div *ngIf="(hotZoneItem.key == data.renderTo)">{{hotZoneItem.text}}</div>
</div>
</nz-form-control>
<nz-form-control [nzSpan]="12">
<button nz-button nzType="dashed" (click)="setIndexBtnClick(data)" style="margin-right: 10px">
<i nz-icon nzType="tool" nzTheme="outline"></i>
配置序号
</button>
<button nz-button nzType="danger" (click)="resetSentence(data)">
<i nz-icon nzType="delete" nzTheme="outline"></i>重置
</button>
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label [nzSpan]="3">音频</nz-form-label>
<nz-form-control [nzSpan]="5">
<app-audio-recorder [audioUrl]="data.audioUrl"
(audioUploaded)="onAudioUploadSuccess($event, data, 'audioUrl')">
</app-audio-recorder>
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label [nzSpan]="3">语句</nz-form-label>
<nz-form-control [nzSpan]="10">
<textarea rows="4" nz-input [(ngModel)]="data.val" (blur)="saveContent(data)"></textarea>
</nz-form-control>
</nz-form-item>
<nz-divider nzText="点击字母设置变红" nzOrientation="left"></nz-divider>
<nz-form-item>
<nz-form-control [nzSpan]="20">
<div *ngFor="let letter of data.letterArr; let j = index" style="float: left;">
<a [ngStyle]="{'float': 'left','font-size': '42px','font-family': 'GOTHICB','color':letter.fontColor, 'margin-right': '2px'}"
(click)="clickLetter(letter, j)">{{letter.val == ' ' ? '&nbsp;' : letter.val}}</a>
</div>
</nz-form-control>
</nz-form-item>
</nz-card>
<ng-template #actionDelSentence>
<button nz-button nzType="danger" (click)="delSentence(item,i)">
<i nz-icon nzType="delete" nzTheme="outline"></i>删除
</button>
</ng-template>
</div>
</nz-card>
<ng-template #actionAdd>
<button nz-button nzType="dashed" class="add-btn" id="add-btn" (click)="addSentence(item)">
<i nz-icon nzType="plus-circle" nzTheme="outline"></i>添加
</button>
</ng-template>
<ng-template #actionSetting>
<button nz-button nzType="dashed" class="add-btn" id="add-btn" (click)="addSentence(item)">
<i nz-icon nzType="plus-circle" nzTheme="outline"></i>添加
</button>
</ng-template>
</div>
</div>
<nz-modal [(nzVisible)]="indexPanelVisible" nzTitle="序号选择" (nzOnCancel)="indexPanelCancel()" (nzOnOk)="indexPanelOk()"
nzOkText="保存">
<nz-radio-group [(ngModel)]="renderTo">
<label nz-radio [nzValue]="option.key"
*ngFor="let option of hotZoneItemArr; let j = index">序号-({{option.text}})</label>
</nz-radio-group>
</div>
\ No newline at end of file
</nz-modal>
</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 { getDefaultLetter, getDefaultSentence } from '../bean/SentenceBean';
import { getDefaultTile } from '../bean/TitleBean';
......@@ -10,12 +12,19 @@ import {Component, EventEmitter, Input, OnDestroy, OnChanges, OnInit, Output, Ap
export class FormComponent implements OnInit, OnChanges, OnDestroy {
// 储存数据用
saveKey = "test_0011";
saveKey = "YM5-17";
// 储存对象
item;
indexPanelVisible = false;
constructor(private appRef: ApplicationRef,private changeDetectorRef: ChangeDetectorRef) {
curSentence;
renderTo = "";
hotZoneItemArr = [];
constructor(private appRef: ApplicationRef, private changeDetectorRef: ChangeDetectorRef) {
}
......@@ -24,13 +33,21 @@ export class FormComponent implements OnInit, OnChanges, OnDestroy {
this.item = {};
this.item.title = getDefaultTile();
this.item.hotZoneItemArr = [];
this.item.sentenceArr = [];
this.curSentence = {};
// 获取存储的数据
(<any> window).courseware.getData((data) => {
(<any>window).courseware.getData((data) => {
if (data) {
this.item = data;
}
console.log('data:' , data);
this.init();
this.changeDetectorRef.markForCheck();
this.changeDetectorRef.detectChanges();
......@@ -50,6 +67,17 @@ export class FormComponent implements OnInit, OnChanges, OnDestroy {
init() {
if (!this.item.title) {
this.item.title = getDefaultTile();
}
if (!this.item.hotZoneItemArr) {
this.item.hotZoneItemArr = [];
}
if (!this.item.sentenceArr) {
this.item.sentenceArr = [];
}
}
......@@ -58,29 +86,56 @@ export class FormComponent implements OnInit, OnChanges, OnDestroy {
* 储存图片数据
* @param e
*/
onImageUploadSuccess(e, key) {
onImageUploadSuccess(e, item, key) {
this.item[key] = e.url;
this.save();
item[key] = e.url;
this.save();
}
/**
* 储存音频数据
* @param e
*/
onAudioUploadSuccess(e, key) {
onAudioUploadSuccess(e, item, key) {
this.item[key] = e.url;
item[key] = e.url;
this.save();
}
saveData(e, item) {
const { bgItem, hotZoneItemArr } = e;
item.bgItem = bgItem;
item.hotZoneItemArr = hotZoneItemArr;
this.save();
}
addSentence(item) {
let sentence = getDefaultSentence();
item.sentenceArr.push(sentence);
this.save();
}
delSentence(item, index) {
if (index !== -1) {
item.sentenceArr.splice(index, 1);
item.sentenceArr = [...item.sentenceArr];
this.save();
}
}
/**
* 储存数据
*/
save() {
(<any> window).courseware.setData(this.item, null, this.saveKey);
(<any>window).courseware.setData(this.item, null, this.saveKey);
this.refresh();
}
......@@ -93,5 +148,101 @@ export class FormComponent implements OnInit, OnChanges, OnDestroy {
}, 1);
}
saveContent(item) {
let val = "";
if(!item.letterArr){
item.letterArr = [];
}
for (let i = 0; i < item.letterArr.length; ++i) {
val += item.letterArr[i].val;
}
//值发生改变,则重新处理数据
if (item.val != val) {
item.letterArr = [];
for (let i = 0; i < item.val.length; ++i) {
let letter = getDefaultLetter();
letter.val = item.val.charAt(i);
item.letterArr.push(letter);
}
}
this.save();
}
resetSentence(item){
item.renderTo = "";
this.save();
}
clickLetter(letter, i) {
if(letter.fontColor == "#000000"){
letter.fontColor = "#c8171e";
}
else{
letter.fontColor = "#000000";
}
this.save();
}
setIndexBtnClick(item) {
this.curSentence = item;
this.renderTo = item.renderTo;
this.indexPanelVisible = true;
this.hotZoneItemArr = [];
for(let i = 0; i < this.item.hotZoneItemArr.length; ++ i){
let hotZoneItem = this.item.hotZoneItemArr[i];
if(item.renderTo == hotZoneItem.key){
this.hotZoneItemArr.push(hotZoneItem);
continue;
}
let exist = false;
for(let j = 0; j < this.item.sentenceArr.length; ++ j){
let sentence = this.item.sentenceArr[j];
if(sentence.renderTo == hotZoneItem.key){
exist = true;
break;
}
}
if(!exist){
this.hotZoneItemArr.push(hotZoneItem);
}
}
this.refresh();
}
indexPanelCancel() {
this.indexPanelVisible = false;
}
indexPanelOk() {
this.indexPanelVisible = false;
this.curSentence.renderTo = this.renderTo;
this.save();
}
}
/**
*
* @param audio 音频
* @param now true-重新开始播放,flase-继续播放
* @param callback
*/
export function playAudio(audio, now = false, callback = null) {
if (audio) {
if (now) {
audio.pause();
audio.currentTime = 0;
}
if (callback) {
audio.onended = () => {
callback();
};
}
audio.play();
}
}
/**
* 暂停播放音频
* @param audio 音频
* @param reset 暂停是否重置:true-是,false-否
* @param callback
*/
export function pauseAudio(audio, reset = false, callback = null) {
if (audio) {
if (reset) {
audio.currentTime = 0;
}
audio.pause();
if (callback) {
callback();
}
}
}
\ No newline at end of file
import { Label, ShapeCircle } from "./Unit";
export class Index {
data;
images;
scaleX = 1;
scaleY = 1;
bg;
constructor(data, images) {
this.data = data;
this.images = images;
}
init(mapScaleX = 1, mapScaleY = 1) {
this.scaleX = mapScaleX;
this.scaleY = mapScaleY;
this.initBg();
this.initPic();
}
private initBg() {
let r = 10 * this.scaleX;
const bg = new ShapeCircle();
bg.setRadius(r);
bg.fillColor = "#000000";
this.bg = bg;
}
private initPic() {
//序号
const index = new Label();
index.text = this.data.text;
index.textAlign = 'center';
index.fontSize = 15;
index.fontName = "FUTURA";
index.fontColor = "#ffffff";
index.setScaleXY(this.scaleX);
index.refreshSize();
index.x = 0;
index.y = 0;
this.bg.addChild(index);
}
}
\ No newline at end of file
import {
Label,
MySprite, ShapeRectNew,
ShapeCircle,
tweenChange,
ShapeRect
} from './Unit';
import {
playAudio,
pauseAudio
} from './AudioUtil';
export class Listening {
audio;
images;
bg;
btnPlay;
btnStop;
playing = false;
constructor(audio, images) {
this.audio = audio;
this.images = images;
}
init(scaleX = 1, scaleY = 1) {
const bg = new ShapeRect();
const btnPlay = new MySprite();
btnPlay.init(this.images.get('play'));
btnPlay.scaleX = scaleX;
btnPlay.scaleY = scaleY;
btnPlay.x = btnPlay.getBoundingBox().width / 2
btnPlay.y = btnPlay.getBoundingBox().height / 2
this.btnPlay = btnPlay;
bg.addChild(btnPlay);
const btnStop = new MySprite();
btnStop.init(this.images.get('stop'));
btnStop.x = btnPlay.x;
btnStop.y = btnPlay.y;
btnStop.alpha = 0;
btnStop.scaleX = scaleX;
btnStop.scaleY = scaleY;
this.btnStop = btnStop;
bg.addChild(btnStop);
bg.setSize(btnPlay.getBoundingBox().width, btnPlay.getBoundingBox().height);
bg.alpha = 0;
this.bg = bg;
}
play(callback = null){
console.log('playing:', this.playing);
if(!this.playing){
this.playAudio(callback);
return this.audio;
}
else{
this.pauseAudio(callback);
}
return null;
}
reset(){
this.btnPlay.alpha = 1;
this.btnStop.alpha = 0;
this.playing = false;
}
playAudio(callback = null){
this.btnPlay.alpha = 0;
this.btnStop.alpha = 1;
playAudio(this.audio, false, callback);
this.playing = true;
return this.audio;
}
pauseAudio(callback){
this.btnPlay.alpha = 1;
this.btnStop.alpha = 0;
this.playing = false;
pauseAudio(this.audio, false, callback)
}
}
\ No newline at end of file
import { constants } from "node:buffer";
import { ShapeRect, ShapeRectNew } from "./Unit";
export class Scrollbar {
scaleX = 1;
scaleY = 1;
/**视窗高度 */
viewHight = 0;
/**总高度 */
hight = 0;
bg: ShapeRect;
shown = true;
scrollbar: ShapeRectNew;
scrollbarBg: ShapeRectNew;
scrollbarInitY = 0;
width = 0;
canMoveDis = 0;
perRowDis = 0;
/**触摸y坐标 */
touchY = 0;
touched = false;
curY = 0;
/**滚动比例 */
scale = 1;
init(scaleX = 1, scaleY = 1, viewHight = 0, hight = 0, width = 0) {
this.scaleX = scaleX;
this.scaleY = scaleY;
this.viewHight = viewHight;
this.hight = hight;
this.width = width;
this.initBg();
this.initView();
}
private initBg() {
const bg = new ShapeRect();
bg.setSize(this.width * this.scaleX, this.viewHight);
bg.alpha = 0;
this.bg = bg;
}
private initView() {
const scrollbarBg = new ShapeRectNew();
scrollbarBg.setSize(this.width * this.scaleX, this.viewHight, 7.5 * this.scaleX);
scrollbarBg.fillColor = "#000000";
scrollbarBg.x = 0;
scrollbarBg.y = 0;
scrollbarBg.alpha = 0;
this.scrollbarBg = scrollbarBg;
this.bg.addChild(scrollbarBg);
const scrollbar = new ShapeRectNew();
scrollbar.setSize(8 * this.scaleX, 0 * this.scaleY, 4 * this.scaleX);
scrollbar.fillColor = "#fef89a";
scrollbar.x = (this.bg.width - scrollbar.width) / 2;
scrollbar.y = 3 * this.scaleY;
scrollbar.visible = false;
this.bg.addChild(scrollbar);
this.scrollbar = scrollbar;
this.scrollbarInitY = scrollbar.y;
}
hide(){
this.bg.visible = false;
this.shown = false;
}
show(){
this.bg.visible = true;
this.shown = true;
}
showScrollbar() {
if (this.hight <= this.viewHight) {
this.scrollbarBg.alpha = 0;
this.scrollbar.visible = false;
}
else {
this.scrollbarBg.alpha = 1;
this.scrollbar.visible = true;
let draghight = this.viewHight - 6 * this.scaleY;
let height = draghight / (this.hight / draghight);
this.scrollbar.height = height;
this.canMoveDis = draghight - height;
if(this.canMoveDis != 0){
this.scale = (this.hight - draghight) / this.canMoveDis
}
}
}
touchScrollbar(my: number) {
this.touchY = my;
this.curY = this.scrollbar.y;
this.touched = true;
}
drag(dis: number, callback) {
if (!this.touched) {
return;
}
let newY = this.curY + dis;
if (newY < this.scrollbarInitY) {
newY = this.scrollbarInitY;
}
else if (newY > this.scrollbarInitY + this.canMoveDis) {
newY = this.scrollbarInitY + this.canMoveDis;
}
this.scrollbar.y = newY;
let tDis = newY - this.curY;
if(callback){
let dis = tDis * this.scale;
callback(dis);
}
}
}
\ No newline at end of file
import {
Label,
MySprite, ShapeRect,
} from './Unit';
import {
playAudio,
pauseAudio
} from './AudioUtil';
export class Title {
titleBg: ShapeRect;
audio;
images;
titleData;
constructor(titleData, images) {
this.titleData = titleData;
this.images = images;
}
init(mapScaleX = 1, mapScaleY = 1) {
let titleBg = new ShapeRect();
//初始化标题1背景
const titePart1Bg = new ShapeRect();
titePart1Bg.fillColor="#763c92";
let pWidth = 117 * mapScaleX;
let pHeight = 65 * mapScaleY;
titePart1Bg.setSize(pWidth, pHeight);
titePart1Bg.setShadow(4,4,0, 'rgba(0, 0, 0, 0.2)');
titePart1Bg.x = 0;
titleBg.addChild(titePart1Bg);
//标题1内容
const part1 = new Label();
part1.text = this.titleData.part1;
part1.textAlign = 'left';
part1.fontSize = 48;
part1.fontName = "BRLNSDB";
part1.fontColor = "#facf46";
part1.setScaleXY(mapScaleX);
part1.refreshSize();
part1.x = pWidth - part1.getBoundingBox().width - 13 * mapScaleX;
part1.y = 5 * mapScaleY + part1.getBoundingBox().height / 2;
titleBg.addChild(part1);
//标题2内容
const part2 = new Label();
part2.text = this.titleData.part2;
part2.fontSize = 36;
part2.fontName = "FUTURA";
part2.fontColor = "#000000";
part2.setScaleXY(mapScaleX);
part2.refreshSize();
part2.x = titePart1Bg.getBoundingBox().width + 11 * mapScaleX;
part2.y = 16 * mapScaleY + part2.getBoundingBox().height / 2;
let height = Math.max(titePart1Bg.getBoundingBox().height, part2.getBoundingBox().height);
let width = titePart1Bg.getBoundingBox().width + part2.getBoundingBox().width + 11 * mapScaleX;
titePart1Bg.y = (height - pHeight)/2;
titleBg.setSize(width, height);
titleBg.alpha = 0;
titleBg.addChild(part2);
this.titleBg = titleBg;
}
getTitleBg(){
return this.titleBg;
}
setAudio(audio){
this.audio = audio;
}
playAudio(callback = null){
playAudio(this.audio, true, callback);
return this.audio;
}
pasueAudio(callback){
pauseAudio(this.audio, true, callback)
}
}
\ No newline at end of file
......@@ -4,7 +4,6 @@ import TWEEN from '@tweenjs/tween.js';
interface AirWindow extends Window {
air: any;
curCtx: any;
curImages: any;
}
declare let window: AirWindow;
......@@ -37,6 +36,7 @@ class Sprite {
export class MySprite extends Sprite {
_width = 0;
......@@ -69,16 +69,6 @@ export class MySprite extends Sprite {
img;
_z = 0;
_showRect;
_bitmapFlag = false;
_offCanvas;
_offCtx;
isCircleStyle = false; // 切成圆形
parent;
_maskSpr;
init(imgObj = null, anchorX: number = 0.5, anchorY: number = 0.5) {
......@@ -89,6 +79,7 @@ export class MySprite extends Sprite {
this.width = this.img.width;
this.height = this.img.height;
}
this.anchorX = anchorX;
......@@ -96,12 +87,9 @@ export class MySprite extends Sprite {
}
setShowRect(rect) {
this._showRect = rect;
}
setShadow(offX, offY, blur, color = 'rgba(0, 0, 0, 0.3)') {
this._shadowFlag = true;
this._shadowColor = color;
this._shadowOffsetX = offX;
......@@ -114,19 +102,6 @@ export class MySprite extends Sprite {
this._radius = r;
}
setMaskSpr(spr) {
this._maskSpr = spr;
this._createOffCtx();
}
_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._offCtx = this._offCanvas.getContext('2d');
}
}
update($event = null) {
if (!this.visible && this.childDepandVisible) {
......@@ -162,22 +137,7 @@ export class MySprite extends Sprite {
this.ctx.transform(1, this.skewX, this.skewY, 1, 0, 0);
}
drawSelf() {
if (this._shadowFlag) {
this.ctx.shadowOffsetX = this._shadowOffsetX;
this.ctx.shadowOffsetY = this._shadowOffsetY;
this.ctx.shadowBlur = this._shadowBlur;
this.ctx.shadowColor = this._shadowColor;
} else {
this.ctx.shadowOffsetX = 0;
this.ctx.shadowOffsetY = 0;
this.ctx.shadowBlur = null;
this.ctx.shadowColor = null;
}
if (this._radius) {
......@@ -186,68 +146,45 @@ export class MySprite extends Sprite {
const w = this.width;
const h = this.height;
this.ctx.beginPath()
this._roundRect(-w / 2, - h / 2, w, h, r * 1 || 0);
this.ctx.clip();
}
this.ctx.lineTo(-w / 2, h / 2); // 创建水平线
this.ctx.arcTo(-w / 2, -h / 2, -w / 2 + r, -h / 2, r);
this.ctx.arcTo(w / 2, -h / 2, w / 2, -h / 2 + r, r);
this.ctx.arcTo(w / 2, h / 2, w / 2 - r, h / 2, r);
this.ctx.arcTo(-w / 2, h / 2, -w / 2, h / 2 - r, r);
if (this.isCircleStyle) {
this.ctx.beginPath()
this.ctx.arc(0, 0, Math.max(this.width, this.height) / 2, 0, Math.PI * 2, false); // 圆形
this.ctx.clip();
}
}
drawSelf() {
if (this.img) {
if (this._showRect) {
const rect = this._showRect;
this.ctx.drawImage(this.img, rect.x, rect.y, rect.width, rect.height, this._offX + rect.x, this._offY + rect.y, rect.width, rect.height);
} else {
if (this._offCtx) {
this._offScreenRender();
} else {
this.ctx.drawImage(this.img, this._offX, this._offY);
}
}
}
}
_offScreenRender() {
if (this._shadowFlag) {
this._offCtx.save();
this.ctx.shadowOffsetX = this._shadowOffsetX;
this.ctx.shadowOffsetY = this._shadowOffsetY;
this.ctx.shadowBlur = this._shadowBlur;
this.ctx.shadowColor = this._shadowColor;
} else {
this.ctx.shadowOffsetX = 0;
this.ctx.shadowOffsetY = 0;
this.ctx.shadowBlur = null;
this.ctx.shadowColor = null;
}
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);
if (this.img) {
this.ctx.drawImage(this.img, this._offX, this._offY);
}
this.ctx.drawImage(this._offCanvas, this._offX, this._offY);
this._offCtx.restore();
}
_roundRect(x, y, w, h, r) {
const min_size = Math.min(w, h);
if (r > min_size / 2) r = min_size / 2;
// 开始绘制
const ctx = this.ctx;
ctx.beginPath();
ctx.moveTo(x + r, y);
ctx.arcTo(x + w, y, x + w, y + h, r);
ctx.arcTo(x + w, y + h, x, y + h, r);
ctx.arcTo(x, y + h, x, y, r);
ctx.arcTo(x, y, x + w, y, r);
ctx.closePath();
}
updateChildren() {
updateChildren() {
if (this.children.length <= 0) { return; }
......@@ -320,13 +257,6 @@ export class MySprite extends Sprite {
}
}
set bitmapFlag(v) {
this._bitmapFlag = v;
}
get bitmapFlag() {
return this._bitmapFlag;
}
set alpha(v) {
this._alpha = v;
if (this.childDepandAlpha) {
......@@ -375,6 +305,7 @@ export class MySprite extends Sprite {
getBoundingBox() {
const getParentData = (item) => {
let px = item.x;
......@@ -412,6 +343,11 @@ export class MySprite extends Sprite {
const width = this.width * Math.abs(data.sx);
const height = this.height * Math.abs(data.sy);
// const x = this.x + this._offX * Math.abs(this.scaleX);
// const y = this.y + this._offY * Math.abs(this.scaleY);
// const width = this.width * Math.abs(this.scaleX);
// const height = this.height * Math.abs(this.scaleY);
return {x, y, width, height};
}
......@@ -611,10 +547,7 @@ export class Label extends MySprite {
this._maxWidth = w;
this.refreshSize();
if (this.width >= w) {
this.scaleX *= w / this.width;
this.scaleY *= w / this.width;
}
setFontSize(this._maxWidth, this);
}
show(callBack = null) {
......@@ -709,6 +642,265 @@ export class Label extends MySprite {
}
export class LabelText {
private _val: string;
private _fontColor = "#000000";
private _underLine: boolean = false;
private _visiable = true;
private _fill = false;
private _width = 0;
private _x = 0;
set val(val) {
this._val = val;
}
get val() {
return this._val;
}
set fontColor(fontColor) {
this._fontColor = fontColor;
}
get fontColor() {
return this._fontColor;
}
set underLine(underLine) {
this._underLine = underLine;
}
get underLine() {
return this._underLine;
}
set visiable(visiable){
this._visiable = visiable;
}
get visiable(){
return this._visiable;
}
set fill(file){
this._fill = file;
}
get fill(){
return this._fill;
}
set width(width){
this._width = width;
}
get width(){
return this._width;
}
set x(val){
this._x = val;
}
get x(){
return this._x;
}
}
export class Label2 extends MySprite {
textArr: Array<LabelText> = [];
fontName = 'Verdana';
textAlign = 'left';
fontSize = 40;
fontColor = '#000000';
fontWeight = 900;
_maxWidth;
outline = 0;
outlineColor = '#ffffff';
_outlineFlag = false;
_outLineWidth;
_outLineColor;
underLineWidth: number;
underLineColor = "#000000";
constructor(ctx = null) {
super(ctx);
this.init();
}
refreshSize() {
this.ctx.save();
this.ctx.font = `${this.fontSize}px ${this.fontName}`;
this.ctx.textAlign = this.textAlign;
this.ctx.textBaseline = 'middle';
this.ctx.fontWeight = this.fontWeight;
let text = "";
let count = 0;
for (let i = 0; i < this.textArr.length; ++i) {
if(this.textArr[i].underLine){
count ++;
continue;
}
text += this.textArr[i].val;
}
if(count > 0){
this._width = this.ctx.measureText(text).width + 36 * count + 4 * (count - 1);
}
else{
this._width = this.ctx.measureText(text).width;
}
this._height = this.fontSize;
this.refreshAnchorOff();
this.ctx.restore();
}
setMaxSize(w) {
this._maxWidth = w;
this.refreshSize();
if (this.width >= w) {
this.scaleX *= w / this.width;
this.scaleY *= w / this.width;
}
}
show(callBack = null) {
this.visible = true;
if (this.alpha >= 1) {
return;
}
const tween = new TWEEN.Tween(this)
.to({ alpha: 1 }, 800)
// .easing(TWEEN.Easing.Quadratic.Out) // Use an easing function to make the animation smooth.
.onComplete(() => {
if (callBack) {
callBack();
}
})
.start(); // Start the tween immediately.
}
setOutline(width = 5, color = '#ffffff') {
this._outlineFlag = true;
this._outLineWidth = width;
this._outLineColor = color;
}
drawText() {
if (!this.textArr) { return; }
this.ctx.font = `${this.fontSize}px ${this.fontName}`;
this.ctx.textAlign = this.textAlign;
this.ctx.textBaseline = 'middle';
this.ctx.fontWeight = this.fontWeight;
var startX = 0;
for (let i = 0; i < this.textArr.length; ++i) {
var text = this.textArr[i];
let width = this.ctx.measureText(text.val).width;
text.width = width;
text.x = startX;
let tempStartX = startX;
let dW = 0;
if (text.underLine && this.underLineWidth > 0) {
this.ctx.save();
this.ctx.beginPath();
this.ctx.lineWidth = this.underLineWidth;
this.ctx.strokeStyle = this.underLineColor;
let lineY = this.fontSize / 2;
this.ctx.moveTo(tempStartX, lineY);
this.ctx.lineTo(tempStartX + 36, lineY);
this.ctx.stroke();
this.ctx.restore();
tempStartX += (36 - width) / 2;
dW = 36 ;
if(this.textArr[i + 1] && this.textArr[i + 1].underLine){
dW += 4 ;
}
}
else{
dW = width;
}
if (this._outlineFlag) {
this.ctx.lineWidth = this._outLineWidth;
this.ctx.strokeStyle = this._outLineColor;
this.ctx.strokeText(text.val, tempStartX, 0);
}
if (this.outline > 0) {
this.ctx.lineWidth = this.outline;
this.ctx.strokeStyle = this.outlineColor;
this.ctx.strokeText(text.val, tempStartX, 0);
}
this.ctx.fillStyle = text.fontColor ? text.fontColor : this.fontColor;
if(text.visiable){
this.ctx.fillText(text.val, tempStartX, 0);
}
startX += dW;
}
}
drawSelf() {
super.drawSelf();
this.drawText();
}
}
export class RichTextOld extends Label {
textArr = [];
......@@ -819,11 +1011,97 @@ export class RichTextOld extends Label {
export class Text {
private _val: string;
private _fontColor = "#000000";
private _bold = false;
private _width = 0;
private _x = 0;
private _y = 0;
private _ext01;
set val(val) {
this._val = val;
}
get val() {
return this._val;
}
set fontColor(fontColor) {
this._fontColor = fontColor;
}
get fontColor() {
return this._fontColor;
}
set bold(bold) {
this._bold = bold;
}
get bold() {
return this._bold;
}
set width(width) {
this._width = width;
}
get width() {
return this._width;
}
get x() {
return this._x;
}
set x(x) {
this._x = x;
}
get y() {
return this._y;
}
set y(y) {
this._y = y;
}
get ext01() {
return this._ext01;
}
set ext01(ext01) {
this._ext01 = ext01;
}
}
export class RichText extends Label {
disH = 30;
offW = 10;
disH = 10;
offW = 0;
boldFontName = 'Verdana';
//显示内容
textArr: Array<Text> = [];
answered = false;
endPos = { offX: 0, offY: 0 };
maxWidth;
data;
constructor(ctx?: any) {
super(ctx);
......@@ -831,64 +1109,182 @@ export class RichText extends Label {
// this.dataArr = dataArr;
}
private getContent() {
const getBlank = () => {
let blank = new Text();
blank.val = ' ';
blank.width = this.ctx.measureText(blank.val).width;
return blank;
}
const selfW = this.maxWidth;
let temp = '';
const row = [];
const w = selfW - 2 * this.offW;
let tempArr = [];
let tempStr = "";
for (let i = 0; i < this.textArr.length; ++i) {
let text = this.textArr[i];
let c = this.textArr[i].val;
var re = /\n/;
if (c.match(re)) {
row.push(tempArr);
temp = ' ' + tempStr;
tempArr = [];
tempArr.push(getBlank());
tempArr.push(this.textArr[i]);
tempStr = "";
}
else {
if (text.val != ' ') {
if (text.bold) {
this.ctx.font = `${this.fontSize}px ${this.boldFontName}`;
}
else {
this.ctx.font = `${this.fontSize}px ${this.fontName}`;
}
this.textArr[i].width = this.ctx.measureText(c).width;
tempStr += text.val;
tempArr.push(this.textArr[i]);
continue
}
if (this.ctx.measureText(temp).width < w && this.ctx.measureText(temp + (tempStr)).width <= w) {
temp += ' ' + tempStr;
tempArr.push(getBlank());
tempArr.push(this.textArr[i]);
} else {
row.push(tempArr);
temp = ' ' + tempStr;
tempArr = [];
tempArr.push(getBlank());
tempArr.push(this.textArr[i]);
}
tempStr = "";
}
drawText() {
if (!this.text) {
return;
}
if (temp != ' ') {
row.push(tempArr);
}
return row;
}
refreshSize() {
this.ctx.save();
this.ctx.font = `${this.fontSize}px ${this.fontName}`;
this.ctx.textAlign = this.textAlign;
this.ctx.textBaseline = 'middle';
this.ctx.fontWeight = this.fontWeight;
this.ctx.fillStyle = this.fontColor;
const row = this.getContent();
const selfW = this.width * this.scaleX;
this._height = this.fontSize * row.length + (row.length - 1) * this.disH;
let mW = 0;
const disH = (this.fontSize + this.disH);
let x = 0;
for (let b = 0; b < row.length; b++) {
const chr = this.text.split(' ');
let temp = '';
const row = [];
const w = selfW - this.offW * 2;
const disH = (this.fontSize + this.disH) * this.scaleY;
let tW = 0;
let textArr = row[b];
for (let i = 0; i < textArr.length; ++i) {
tW += textArr[i].width;
for (const c of chr) {
if (this.ctx.measureText(temp).width < w && this.ctx.measureText(temp + (c)).width <= w) {
temp += ' ' + c;
} else {
row.push(temp);
temp = ' ' + c;
textArr[i].x = x;
textArr[i].y = b * disH;
x += textArr[i].width;
}
if (tW > mW) {
mW = tW;
}
if(b == row.length - 1){
this.endPos.offX = x;
this.endPos.offY = b * disH;
}
x = 0;
}
row.push(temp);
const x = 0;
const y = -row.length * disH / 2;
// for (let b = 0 ; b < row.length; b++) {
// this.ctx.strokeText(row[b], x, y + ( b + 1 ) * disH ); // 每行字体y坐标间隔20
// }
this._width = mW;
this.ctx.restore();
}
drawText() {
if (!this.textArr) {
return;
}
this.ctx.font = `${this.fontSize}px ${this.fontName}`;
this.ctx.textAlign = this.textAlign;
this.ctx.textBaseline = 'bottom';
const row = this.getContent();
const disH = (this.fontSize + this.disH);
let x = 0;
const y = -row.length * disH / 2;
if (this._outlineFlag) {
this.ctx.lineWidth = this._outLineWidth;
this.ctx.strokeStyle = this._outLineColor;
for (let b = 0 ; b < row.length; b++) {
this.ctx.strokeText(row[b], x, y + ( b + 1 ) * disH ); // 每行字体y坐标间隔20
for (let b = 0; b < row.length; b++) {
let textArr = row[b];
let temp = "";
for (let i = 0; i < textArr.length; ++i) {
temp += textArr[i].val;
}
this.ctx.strokeText(temp, x, y + (b + 1) * disH); // 每行字体y坐标间隔
x = 0;
}
// this.ctx.strokeText(this.text, 0, 0);
}
x = 0;this.ctx.font = `${this.fontSize}px ${this.boldFontName}`;
// this.ctx.fillStyle = '#ff7600';
for (let b = 0 ; b < row.length; b++) {
this.ctx.fillText(row[b], x, y + ( b + 1 ) * disH ); // 每行字体y坐标间隔20
for (let b = 0; b < row.length; b++) {
let textArr = row[b];
for (let i = 0; i < textArr.length; ++i) {
if (textArr[i].bold) {
this.ctx.font = `${this.fontSize}px ${this.boldFontName}`;
}
else {
this.ctx.font = `${this.fontSize}px ${this.fontName}`;
}
this.ctx.fillStyle = textArr[i].fontColor;
this.ctx.fillText(textArr[i].val, x, y + (b + 1) * disH); // 每行字体y坐标间隔
textArr[i].x = x;
textArr[i].y = b * disH;
x += textArr[i].width;
}
x = 0;
}
}
......@@ -1029,28 +1425,28 @@ export class ShapeRectNew extends MySprite {
ctx.save();
ctx.beginPath(0);
// 从右下角顺时针绘制,弧度从0到1/2PI
ctx.arc(width - radius + this._offX, height - radius + this._offY, radius, 0, Math.PI / 2);
ctx.arc(width - radius, height - radius, radius, 0, Math.PI / 2);
// 矩形下边线
ctx.lineTo(radius + this._offX, height + this._offY);
ctx.lineTo(radius, height);
// 左下角圆弧,弧度从1/2PI到PI
ctx.arc(radius + this._offX, height - radius + this._offY, radius, Math.PI / 2, Math.PI);
ctx.arc(radius, height - radius, radius, Math.PI / 2, Math.PI);
// 矩形左边线
ctx.lineTo(0 + this._offX, radius + this._offY);
ctx.lineTo(0, radius);
// 左上角圆弧,弧度从PI到3/2PI
ctx.arc(radius + this._offX, radius + this._offY, radius, Math.PI, Math.PI * 3 / 2);
ctx.arc(radius, radius, radius, Math.PI, Math.PI * 3 / 2);
// 上边线
ctx.lineTo(width - radius + this._offX, 0 + this._offY);
ctx.lineTo(width - radius, 0);
// 右上角圆弧
ctx.arc(width - radius + this._offX, radius + this._offY, radius, Math.PI * 3 / 2, Math.PI * 2);
ctx.arc(width - radius, radius, radius, Math.PI * 3 / 2, Math.PI * 2);
// 右边线
ctx.lineTo(width + this._offX, height - radius + this._offY);
ctx.lineTo(width, height - radius);
ctx.closePath();
if (this.fill) {
......@@ -1085,7 +1481,7 @@ export class MyAnimation extends MySprite {
loop = false;
playEndFunc;
delayPerUnit = 0.07;
delayPerUnit = 1;
restartFlag = false;
reverseFlag = false;
......@@ -1174,9 +1570,8 @@ export class MyAnimation extends MySprite {
this.frameArr[this.frameIndex].visible = true;
if (this.playEndFunc) {
const func = this.playEndFunc;
this.playEndFunc();
this.playEndFunc = null;
func();
}
}
......@@ -1235,21 +1630,55 @@ export class MyAnimation extends MySprite {
}
export class Line extends MySprite{
lineColor = '#ffffff';
lineWidth = 10;
_sX = 0;
// --------=========== util func =============-------------
_sY = 0;
_tX = 0;
export function tweenChange(item, obj, time = 0.8, callBack = null, easing = null, update = null) {
_tY = 0;
const tween = createTween(item, obj, time, callBack, easing, update)
tween.start();
setLine(sX, sY, tX, tY){
this._sX = sX;
return tween;
this._sY = sY;
this._tX = tX;
this._tY = tY;
}
drawLine() {
this.ctx.beginPath();
this.ctx.moveTo(this._sX, this._sY);
this.ctx.lineTo(this._tX, this._tY);
this.ctx.lineWidth = this.lineWidth;
this.ctx.strokeStyle = this.lineColor;
this.ctx.stroke();
}
drawSelf() {
super.drawSelf();
this.drawLine();
}
}
export function createTween(item, obj, time = 0.8, callBack = null, easing = null, update = null) {
// --------=========== util func =============-------------
export function tweenChange(item, obj, time = 0.8, callBack = null, easing = null, update = null) {
const tween = new TWEEN.Tween(item).to(obj, time * 1000);
......@@ -1266,28 +1695,15 @@ export function createTween(item, obj, time = 0.8, callBack = null, easing = nul
update(a, b);
});
}
return tween;
}
export function tweenQueue(arr) {
const showOneTween = () => {
const tween = arr.shift();
if (arr.length > 0) {
tween.onComplete( () => {
showOneTween();
});
}
tween.start();
};
showOneTween();
tween.start();
return tween;
}
export function rotateItem(item, rotation, time = 0.8, callBack = null, easing = null, loop = false) {
export function rotateItem(item, rotation, time = 0.8, callBack = null, easing = null) {
const tween = new TWEEN.Tween(item).to({ rotation }, time * 1000);
......@@ -1295,14 +1711,7 @@ export function rotateItem(item, rotation, time = 0.8, callBack = null, easing =
tween.onComplete(() => {
callBack();
});
} else if (loop) {
const speed = (rotation - item.rotation) / time;
tween.onComplete(() => {
item.rotation %= 360;
rotateItem(item, item.rotation + speed * time, time, null, easing, true);
});
}
if (easing) {
tween.easing(easing);
}
......@@ -1585,7 +1994,7 @@ export function getPosDistance(sx, sy, ex, ey) {
return len;
}
export function delayCall(second, callback) {
export function delayCall(callback, second) {
const tween = new TWEEN.Tween(this)
.delay(second * 1000)
.onComplete(() => {
......@@ -1619,15 +2028,10 @@ export function formatTime(fmt, date) {
return fmt;
}
export function getMinScale(item, maxW, maxH = null) {
if (!maxH) {
maxH = maxW;
}
const sx = maxW / item.width;
const sy = maxH / item.height;
export function getMinScale(item, maxLen) {
const sx = maxLen / item.width;
const sy = maxLen / item.height;
const minS = Math.min(sx, sy);
return minS;
}
......@@ -1670,46 +2074,6 @@ export function jelly(item, time = 0.7) {
run();
}
export function jellyPop(item, time) {
if (item.jellyTween) {
TWEEN.remove(item.jellyTween);
}
const t = time / 6;
const baseSX = item.scaleX;
const baseSY = item.scaleY;
let index = 0;
const run = () => {
if (index >= arr.length) {
item.jellyTween = null;
return;
}
const data = arr[index];
const t = tweenChange(item, {scaleX: data[0], scaleY: data[1]}, data[2], () => {
index ++;
run();
}, TWEEN.Easing.Sinusoidal.InOut);
item.jellyTween = t;
};
const arr = [
[baseSX * 1.3, baseSY * 1.3, t],
[baseSX * 0.85, baseSY * 0.85, t * 1],
[baseSX * 1.1, baseSY * 1.1, t * 1],
[baseSX * 0.95, baseSY * 0.95, t * 1],
[baseSX * 1.02, baseSY * 1.02, t * 1],
[baseSX * 1, baseSY * 1, t * 1],
];
item.setScaleXY(0);
run();
}
export function showPopParticle(img, pos, parent, num = 20, minLen = 40, maxLen = 80, showTime = 0.4) {
......@@ -1811,41 +2175,52 @@ export function shake(item, time = 0.5, callback = null, rate = 1) {
}
export function getMaxScale(item, maxW, maxH) {
const sx = maxW / item.width;
const sy = maxH / item.height;
const maxS = Math.max(sx, sy);
return maxS;
}
// --------------- custom class --------------------
export function createSprite(key) {
const image = window.curImages;
const spr = new MySprite();
spr.init(image.get(key));
return spr;
}
/**
* 根据父级组件大小设置字体大小
* @param parWidth
* @param label
*/
export function setFontSize(maxWidth : number, label){
export class MyVideo extends MySprite {
if(!label){
return;
}
element;
label.refreshSize();
initVideoElement(videoElement) {
let w = label.getBoundingBox().width;
this.element = videoElement;
this.width = this.element.videoWidth;
this.height = this.element.videoHeight;
//如果宽度超过,则缩小字体
if(w > maxWidth){
shrinkFont(maxWidth, label);
}
}
this.element.currentTime = 0.01;
export function shrinkFont(maxWidth : number, label){
if(!label){
return;
}
label.refreshSize();
drawSelf() {
super.drawSelf();
let w = label.getBoundingBox().width;
this.ctx.drawImage(this.element, 0, 0, this.width, this.height);
}
}
//如果宽度超过,则缩小字体
if(w > maxWidth){
let fontSize = label.fontSize;
fontSize = fontSize - 1;
// --------------- custom class --------------------
label.fontSize = fontSize;
label.refreshSize();
shrinkFont(maxWidth, label);
}
}
\ No newline at end of file
......@@ -17,3 +17,44 @@
src: url("../../assets/font/BRLNSDB.TTF") ;
}
@font-face
{
font-family: 'FUTURAB';
src: url("../../assets/font/FUTURAB.TTF") ;
}
@font-face
{
font-family: 'GOTHIC';
src: url("../../assets/font/GOTHIC.TTF") ;
}
@font-face
{
font-family: 'GOTHICB';
src: url("../../assets/font/GOTHICB.TTF") ;
}
@font-face
{
font-family: 'TCB';
src: url("../../assets/font/TCB.TTF") ;
}
@font-face
{
font-family: 'FUTURA';
src: url("../../assets/font/Futura.ttc") ;
}
@font-face
{
font-family: 'FuturaStd';
src: url("../../assets/font/FuturaStd-Medium.otf") ;
}
@font-face
{
font-family: 'ahronbd-1';
src: url("../../assets/font/ahronbd-1.ttf") ;
}
\ No newline at end of file
import {Component, ElementRef, ViewChild, OnInit, Input, OnDestroy, HostListener} from '@angular/core';
import { Component, ElementRef, ViewChild, OnInit, OnDestroy, HostListener } from '@angular/core';
import {
Label,
MySprite, tweenChange,
MySprite, RichText, ShapeRect,
Text
} from './Unit';
import {res, resAudio} from './resources';
import { res, resAudio } from './resources';
import {Subject} from 'rxjs';
import {debounceTime} from 'rxjs/operators';
import { Subject } from 'rxjs';
import { debounceTime } from 'rxjs/operators';
import TWEEN from '@tweenjs/tween.js';
import { getDefaultTile } from '../bean/TitleBean';
import { Listening } from './Listening';
import { Title } from './Title';
import { Index } from './Index';
import { Scrollbar } from './ScrollBar';
......@@ -22,8 +27,8 @@ import TWEEN from '@tweenjs/tween.js';
})
export class PlayComponent implements OnInit, OnDestroy {
@ViewChild('canvas', {static: true }) canvas: ElementRef;
@ViewChild('wrap', {static: true }) wrap: ElementRef;
@ViewChild('canvas', { static: true }) canvas: ElementRef;
@ViewChild('wrap', { static: true }) wrap: ElementRef;
// 数据
data;
......@@ -57,17 +62,29 @@ export class PlayComponent implements OnInit, OnDestroy {
canvasLeft;
canvasTop;
saveKey = 'test_0011';
saveKey = 'YM5-17';
btnLeft;
btnRight;
pic1;
pic2;
canTouch = true;
curPic;
/**标题*/
title;
listening;
curAudio;
indexArr = [];
scrollbar: Scrollbar;
sentenceArr = [];
startIndex = 0;
sentenceBg: ShapeRect;
posArr = [];
@HostListener('window:resize', ['$event'])
onResize(event) {
......@@ -80,13 +97,13 @@ export class PlayComponent implements OnInit, OnDestroy {
this.data = {};
// 获取数据
const getData = (<any> window).courseware.getData;
const getData = (<any>window).courseware.getData;
getData((data) => {
if (data && typeof data == 'object') {
this.data = data;
}
// console.log('data:' , data);
console.log('data:', data);
// 初始化 各事件监听
this.initListener();
......@@ -341,7 +358,7 @@ export class PlayComponent implements OnInit, OnDestroy {
return new Promise((resolve, reject) => {
const audio = new Audio();
audio.oncanplay = (a) => {
resolve();
resolve(a);
};
audio.onerror = () => {
reject();
......@@ -443,9 +460,8 @@ export class PlayComponent implements OnInit, OnDestroy {
*/
initDefaultData() {
if (!this.data.pic_url) {
this.data.pic_url = 'assets/play/default/pic.jpg';
this.data.pic_url_2 = 'assets/play/default/pic.jpg';
if (!this.data.title) {
this.data.title = getDefaultTile();
}
}
......@@ -455,8 +471,9 @@ export class PlayComponent implements OnInit, OnDestroy {
*/
initImg() {
this.addUrlToImages(this.data.pic_url);
this.addUrlToImages(this.data.pic_url_2);
if (this.data.bgItem && this.data.bgItem.url) {
this.addUrlToImages(this.data.bgItem.url);
}
}
......@@ -465,13 +482,32 @@ export class PlayComponent implements OnInit, OnDestroy {
*/
initAudio() {
// 音频资源
this.addUrlToAudioObj(this.data.audio_url);
this.addUrlToAudioObj(this.data.audio_url_2);
// 音效
this.addUrlToAudioObj('click', this.rawAudios.get('click'), 0.3);
//标题发音
if (this.data.title.audioUrl) {
this.addUrlToAudioObj('titleAudio', this.data.title.audioUrl);
}
//听力材料
if (this.data.listenAudioUrl) {
this.addUrlToAudioObj('listenAudio', this.data.listenAudioUrl);
}
if (this.data.sentenceArr) {
for (let i = 0; i < this.data.sentenceArr.length; ++i) {
let sentence = this.data.sentenceArr[i];
if (sentence.audioUrl) {
this.addUrlToAudioObj(sentence.audioUrl);
}
}
}
}
......@@ -486,14 +522,15 @@ export class PlayComponent implements OnInit, OnDestroy {
const s = Math.min(sx, sy);
this.mapScale = s;
// this.mapScale = sx;
// this.mapScale = sy;
this.renderArr = [];
this.indexArr = [];
this.sentenceArr = [];
this.startIndex = 0;
this.posArr = [];
}
......@@ -503,131 +540,198 @@ export class PlayComponent implements OnInit, OnDestroy {
*/
initView() {
this.initPic();
this.initTitle();
this.initBottomPart();
this.initPic();
this.initSentence();
}
initBottomPart() {
const btnLeft = new MySprite();
btnLeft.init(this.images.get('btn_left'));
btnLeft.x = this.canvasWidth - 150 * this.mapScale;
btnLeft.y = this.canvasHeight - 100 * this.mapScale;
btnLeft.setScaleXY(this.mapScale);
if (!this.data.listenAudioUrl) {
return;
}
this.renderArr.push(btnLeft);
const listening = new Listening(this.audioObj['listenAudio'], this.images);
listening.init(this.mapScale, this.mapScale);
this.btnLeft = btnLeft;
listening.bg.x = this.canvasWidth - listening.bg.width - 28 * this.mapScale;
listening.bg.y = this.canvasHeight - listening.bg.height - 6.5 * this.mapScale;
this.listening = listening;
this.renderArr.push(listening.bg);
}
initTitle() {
const title = new Title(this.data.title, this.images);
const btnRight = new MySprite();
btnRight.init(this.images.get('btn_right'));
btnRight.x = this.canvasWidth - 50 * this.mapScale;
btnRight.y = this.canvasHeight - 100 * this.mapScale;
btnRight.setScaleXY(this.mapScale);
title.init(this.mapScale, this.mapScale);
title.audio = this.audioObj['titleAudio'];
this.title = title;
this.renderArr.push(btnRight);
title.titleBg.y = 0;
title.titleBg.x = 0;
this.btnRight = btnRight;
this.renderArr.push(title.titleBg);
}
initPic() {
const maxW = this.canvasWidth * 0.7;
const pic1 = new MySprite();
pic1.init(this.images.get(this.data.pic_url));
pic1.x = this.canvasWidth / 2;
pic1.y = this.canvasHeight / 2;
pic1.setScaleXY(maxW / pic1.width);
this.renderArr.push(pic1);
this.pic1 = pic1;
if (!this.data.bgItem.url) {
return;
}
let width = 371 * this.mapScale;
let height = 539 * this.mapScale;
const label1 = new Label();
label1.text = this.data.text;
label1.textAlign = 'center';
label1.fontSize = 50;
label1.fontName = 'BRLNSDB';
label1.fontColor = '#ffffff';
const bg = new ShapeRect();
bg.setSize(width, height);
bg.alpha = 0;
this.renderArr.push(bg);
bg.x = this.canvasWidth / 2 + 194 * this.mapScale;
bg.y = (this.canvasHeight - height) / 2;
pic1.addChild(label1);
const pic = new MySprite();
pic.init(this.images.get(this.data.bgItem.url));
const rate1 = width / pic.width;
const rate2 = height / pic.height;
const rate = Math.min(rate1, rate2);
pic.setScaleXY(rate);
pic.x = width / 2;
pic.y = height / 2;
bg.addChild(pic);
const getIndex = (item) => {
let index = new Index(item, this.images);
index.init(this.mapScale, this.mapScale);
return index;
}
const pic2 = new MySprite();
pic2.init(this.images.get(this.data.pic_url_2));
pic2.x = this.canvasWidth / 2 + this.canvasWidth;
pic2.y = this.canvasHeight / 2;
pic2.setScaleXY(maxW / pic2.width);
let hotZoneItemArr = this.data.hotZoneItemArr;
for (let i = 0; i < hotZoneItemArr.length; ++i) {
let hotZoneItem = hotZoneItemArr[i];
let rect = hotZoneItem.rect;
let index = getIndex(hotZoneItem);
index.bg.x = (hotZoneItem.rect.x + rect.width / 2) / hotZoneItem.mapScale * this.mapScale;
index.bg.y = (hotZoneItem.rect.y + rect.height / 2) / hotZoneItem.mapScale * this.mapScale;
this.indexArr.push(index);
index.data = hotZoneItem;
bg.addChild(index.bg);
}
}
this.renderArr.push(pic2);
this.pic2 = pic2;
startY = 0;
initY = 0;
initSentence() {
this.curPic = pic1;
}
let width = 790 * this.mapScale;
let startY = this.canvasHeight / 2 - 265 * this.mapScale;
btnLeftClicked() {
if (this.data.titleVal) {
const title = new Label();
title.text = this.data.titleVal;
title.textAlign = "center";
title.fontSize = 48;
title.fontColor = "#763c92";
title.setScaleXY(this.mapScale);
title.fontName = "GOTHICB";
title.refreshSize();
title.x = width / 2;
title.y = this.canvasHeight / 2 - 285 * this.mapScale + title.getBoundingBox().height / 2;
this.lastPage();
}
this.renderArr.push(title);
btnRightClicked() {
startY = this.canvasHeight / 2 - 204 * this.mapScale;
}
this.nextPage();
}
const getTextArr = (index, sentence) => {
let textArr = [];
lastPage() {
let text = new Text();
text.val = index + ". ";
text.fontColor = "#000000";
textArr.push(text);
if (this.curPic == this.pic1) {
return;
for (let i = 0; i < sentence.letterArr.length; ++i) {
let text = new Text();
text.val = sentence.letterArr[i].val;
text.fontColor = sentence.letterArr[i].fontColor;
textArr.push(text);
}
return textArr;
}
this.canTouch = false;
this.startY = startY;
this.initY = startY;
let viewHight = 560 * this.mapScale;
const moveLen = this.canvasWidth;
tweenChange(this.pic1, {x: this.pic1.x + moveLen}, 1);
tweenChange(this.pic2, {x: this.pic2.x + moveLen}, 1, () => {
this.canTouch = true;
this.curPic = this.pic1;
});
}
const bg = new ShapeRect();
bg.setSize(700 * this.mapScale, viewHight);
this.renderArr.push(bg);
bg.x = 36 * this.mapScale;
bg.y = startY;
bg.alpha = 0;
this.sentenceBg = bg;
nextPage() {
let y = 0;
if (this.curPic == this.pic2) {
return;
}
let dH = 24 * this.mapScale;
this.canTouch = false;
let totalHeight = 0;
const moveLen = this.canvasWidth;
tweenChange(this.pic1, {x: this.pic1.x - moveLen}, 1);
tweenChange(this.pic2, {x: this.pic2.x - moveLen}, 1, () => {
this.canTouch = true;
this.curPic = this.pic2;
});
}
for (let i = 0; i < this.data.sentenceArr.length; ++i) {
let sentence = this.data.sentenceArr[i];
if(!sentence.renderTo){
continue;
}
this.posArr.push(y);
const text = new RichText();
text.fontColor = "#000000";
text.fontSize = 36;
text.fontName = "GOTHICB";
text.textArr = getTextArr(i + 1, sentence);
text.disH = 20;
text.maxWidth = bg.width;
text.setScaleXY(this.mapScale);
text.refreshSize();
text.y = y + text.getBoundingBox().height / 2;
text.x = 0;
text.alpha = 0;
text.data = sentence;
//如果超过视窗,则不显示
if (text.y > viewHight) {
//text.visible = false;
}
pic1Clicked() {
this.playAudio(this.data.audio_url);
}
bg.addChild(text);
pic2Clicked() {
this.playAudio(this.data.audio_url_2);
}
this.sentenceArr.push(text);
y += text.getBoundingBox().height + dH;
totalHeight += text.getBoundingBox().height + dH;
}
let scrollbar = new Scrollbar();
scrollbar.init(this.mapScale, this.mapScale, viewHight, totalHeight - dH, 14);
scrollbar.showScrollbar();
//scrollbar.hide();
scrollbar.bg.x = this.canvasWidth / 2 + 161 * this.mapScale;
scrollbar.bg.y = this.canvasHeight / 2 - 231 * this.mapScale;
this.renderArr.push(scrollbar.bg);
this.scrollbar = scrollbar;
}
mapDown(event) {
......@@ -636,23 +740,47 @@ export class PlayComponent implements OnInit, OnDestroy {
return;
}
if ( this.checkClickTarget(this.btnLeft) ) {
this.btnLeftClicked();
if (this.curAudio) {
if (this.curAudio == this.listening.audio) {
this.listening.pauseAudio();
}
else {
this.curAudio.pause();
}
}
if (this.listening && this.checkClickTarget(this.listening.bg)) {
this.curAudio = this.listening.play(() => {
this.listening.reset();
});
return;
}
if ( this.checkClickTarget(this.btnRight) ) {
this.btnRightClicked();
if (this.checkClickTarget(this.title.titleBg)) {
this.curAudio = this.title.playAudio();
return;
}
if ( this.checkClickTarget(this.pic1) ) {
this.pic1Clicked();
let clicked = false;
for (let i = 0; i < this.indexArr.length; ++i) {
let index = this.indexArr[i];
if (this.checkClickTarget(index.bg)) {
clicked = true;
this.clickIndex(index);
break;
}
}
if (clicked) {
return;
}
if ( this.checkClickTarget(this.pic2) ) {
this.pic2Clicked();
if (this.scrollbar.shown && this.checkClickTarget(this.scrollbar.scrollbar)) {
this.scrollbar.touchScrollbar(this.my);
return;
}
......@@ -660,9 +788,51 @@ export class PlayComponent implements OnInit, OnDestroy {
mapMove(event) {
if (this.scrollbar.touched) {
let dis = this.my - this.scrollbar.touchY;
this.scrollbar.drag(dis, (dis) => {
let d = this.initY - (this.startY - dis);
let dH = 24 * this.mapScale;
let tH = 0;
if(dis > 0){
for(let i = 0; i < this.sentenceArr.length; ++ i){
let sentence = this.sentenceArr[i];
tH += sentence.getBoundingBox().height + dH;
if(tH < d){
sentence.alpha = 0;
this.sentenceBg.y = this.startY - dis;
}
else{
break;
}
}
}
else{
}
});
}
}
mapUp(event) {
if (this.scrollbar.touched) {
this.scrollbar.touched = false;
this.startY = this.sentenceBg.y;
return;
}
}
......@@ -686,5 +856,45 @@ export class PlayComponent implements OnInit, OnDestroy {
}
clickIndex(indexItem) {
const getSentence = (key) => {
let item;
for (let i = 0; i < this.data.sentenceArr.length; ++i) {
let sentence = this.data.sentenceArr[i];
if (sentence.renderTo == key) {
item = sentence;
break;
}
}
return item;
}
let sentence = indexItem.sentence;
if (!sentence) {
sentence = getSentence(indexItem.data.key);
indexItem.sentence = sentence;
}
this.curAudio = this.playAudio(sentence.audioUrl, true);
for(let i = 0; i < this.sentenceArr.length; ++ i){
let sentenceSpr = this.sentenceArr[i];
if(sentenceSpr.data.renderTo == sentence.renderTo){
sentenceSpr.alpha = 1;
break;
}
}
}
}
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"],
['play', "assets/play/play.png"],
['stop', "assets/play/stop.png"],
['titlePart1Bg', "assets/play/title-part1-bg.png"],
['close', "assets/play/close.png"],
['overturn', "assets/play/overturn.png"],
['box', "assets/play/box.png"],
];
......@@ -12,7 +13,10 @@ const res = [
const resAudio = [
['click', "assets/play/music/click.mp3"],
['back', "assets/play/music/back.mp3"],
['waves', "assets/play/music/waves.mp3"],
['turn', "assets/play/music/turn.mp3"],
['page', "assets/play/music/page.mp3"],
];
export {res, resAudio};
This source diff could not be displayed because it is too large. You can view the blob instead.
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