Commit 97f314e5 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": "9854d767-42ac-423c-9c7c-778ed3e26e6e"
}
}
\ No newline at end of file
import { Obj } from "./ObjBean";
export class ExercisesBean extends Obj {
answerArr = [];
}
export function getDefaultExercises() {
let exercises = new ExercisesBean();
return exercises;
}
export class AnswerBean extends Obj {
index = 0;
}
export function getDefaultAnswer(){
let answer = new AnswerBean();
return answer;
}
export class Obj{
/**音频材料 */
audioUrl: String;
/**图片材料 */
picUrl:String;
val : String;
}
\ 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
<div class="model-content">
<div nz-row>
<div nz-col [nzSpan]="24">
<nz-form-item>
<nz-form-label [nzSpan]="2" nzFor="part1">标题1</nz-form-label>
<nz-form-control [nzSpan]="4">
<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-label [nzSpan]="2" nzFor="part2">标题2</nz-form-label>
<nz-form-control [nzSpan]="4">
<input nz-input type="text" id="part2" [(ngModel)]="item.title.part2" (blur)="save()">
</nz-form-control>
<nz-form-label [nzSpan]="2" nzFor="audioUrl">发音</nz-form-label>
<nz-form-control [nzSpan]="4">
<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>
<div nz-row>
<div *ngFor="let data of item.exercisesArr; let i=index" style="padding: 0.5vw;">
<nz-form-item>
<nz-form-label [nzSpan]="2" nzFor="audioUrl">听力材料</nz-form-label>
<nz-form-control [nzSpan]="4">
<app-audio-recorder id="data.audioUrl" [audioUrl]="data.audioUrl"
(audioUploaded)="onAudioUploadSuccess($event, data, 'audioUrl')">
</app-audio-recorder>
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label [nzSpan]="2">图片</nz-form-label>
<nz-form-control [nzSpan]="4">
<app-upload-image-with-preview style="width: 100%" [picUrl]="data.picUrl"
(imageUploaded)="onImageUploadSuccess($event, data, 'picUrl')">
</app-upload-image-with-preview>
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label [nzSpan]="2">语句</nz-form-label>
<nz-form-control [nzSpan]="10">
<textarea rows="4" nz-input [(ngModel)]="data.val" (ngModelChange)="check(data.val, 9)"
(blur)="saveContent(data)"></textarea>
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-control [nzSpan]="15">
<nz-table #answerTable nzBordered nzTitle="单词拆分列表" [nzData]="data.answerArr" [nzShowPagination]=false>
<thead>
<tr>
<th>序号</th>
<th>单词</th>
<th>音频</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let row of answerTable.data;let j = index">
<td>{{ j+1 }}</td>
<td>
{{row.val}}
</td>
<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>
<td>
<app-audio-recorder [audioUrl]="row.audioUrl"
(audioUploaded)="onAudioUploadSuccess($event, row, 'audioUrl')">
</app-audio-recorder>
</td>
</tr>
</tbody>
</nz-table>
</nz-form-control>
</nz-form-item>
</div>
<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>
\ No newline at end of file
</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 { NzMessageService } from 'ng-zorro-antd/message';
import { getDefaultAnswer, getDefaultExercises } from '../bean/Exercises';
import { getDefaultTile } from '../bean/TitleBean';
......@@ -10,12 +13,13 @@ import {Component, EventEmitter, Input, OnDestroy, OnChanges, OnInit, Output, Ap
export class FormComponent implements OnInit, OnChanges, OnDestroy {
// 储存数据用
saveKey = "test_0011";
saveKey = "YM5-16";
// 储存对象
item;
length = 0;
constructor(private appRef: ApplicationRef,private changeDetectorRef: ChangeDetectorRef) {
constructor(private appRef: ApplicationRef, private changeDetectorRef: ChangeDetectorRef, private message: NzMessageService) {
}
......@@ -24,13 +28,17 @@ export class FormComponent implements OnInit, OnChanges, OnDestroy {
this.item = {};
this.item.title = getDefaultTile();
this.item.exercisesArr = [];
// 获取存储的数据
(<any> window).courseware.getData((data) => {
(<any>window).courseware.getData((data) => {
if (data) {
this.item = data;
}
console.log(this.item);
this.init();
this.changeDetectorRef.markForCheck();
this.changeDetectorRef.detectChanges();
......@@ -50,7 +58,27 @@ export class FormComponent implements OnInit, OnChanges, OnDestroy {
init() {
if (!this.item.title) {
this.item.title = getDefaultTile();
}
if (!this.item.exercisesArr) {
this.item.exercisesArr = [];
}
let len = this.item.exercisesArr.length;
for (let i = len; i < 1; ++i) {
this.item.exercisesArr.push(getDefaultExercises());
}
this.length = this.item.exercisesArr.length;
for(let i = 0; i < this.length; ++ i){
if(!this.item.exercisesArr[i]){
this.item.exercisesArr[i] = getDefaultExercises();
}
}
}
......@@ -58,19 +86,19 @@ 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();
}
......@@ -80,7 +108,7 @@ export class FormComponent implements OnInit, OnChanges, OnDestroy {
* 储存数据
*/
save() {
(<any> window).courseware.setData(this.item, null, this.saveKey);
(<any>window).courseware.setData(this.item, null, this.saveKey);
this.refresh();
}
......@@ -93,5 +121,96 @@ export class FormComponent implements OnInit, OnChanges, OnDestroy {
}, 1);
}
saveContent(item) {
if (!this.check(item.val, 9)) {
return;
}
let answerArr = [];
if (item.val) {
let chr = item.val.split(/\s+|\?|\.|\,|\!/);
for (let c of chr) {
if (c) {
let answer = getDefaultAnswer();
answer.val = c;
answerArr.push(answer);
}
}
}
item.answerArr = [...answerArr];
this.length = item.answerArr.length;
this.save();
}
check(value, maxLength: number) {
if (!value) {
return true;
}
const chr = value.split(/\s+|\?|\.|\,|\!/);
let count = 0;
for (const c of chr) {
if (c) {
count += 1;
}
}
if (count <= maxLength) {
return true;
}
else {
let id = this.message.error('超过' + maxLength + '个单词', { nzDuration: 0 }).messageId;
setTimeout(() => {
this.message.remove(id);
}, 2000);
return false;
}
}
saveIndex(exersices, item) {
let canSave = true;
if (!item.index) {
canSave = false;
}
else {
for (let i = 0; i < exersices.answerArr.length; ++i) {
let answer = exersices.answerArr[i];
if (item == answer) {
continue;
}
else if (answer.index == item.index) {
canSave = false;
break;
}
}
}
if (canSave) {
this.save();
}
else {
let id = this.message.error('请录入不重复的答题顺序', { nzDuration: 0 }).messageId;
setTimeout(() => {
this.message.remove(id);
}, 2500);
}
}
}
/**
*
* @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 { MySprite, ShapeRect, tweenChange } from "./Unit";
export class BigCard{
data;
images;
scaleX = 1;
scaleY = 1;
bg : ShapeRect;
closePic : MySprite;
shown = false;
contentSpr;
init(images, scaleX = 1, scaleY = 1) {
this.images = images;
this.scaleX = scaleX;
this.scaleY = scaleY;
this.initView();
}
private initView(){
let w = 1021 * this.scaleX;
let h = 600 * this.scaleY
const bg = new ShapeRect();
bg.setSize(w, h);
this.bg = bg;
this.initCard();
this.closePic = this.getColsePic(this.bg);
this.bg.setScaleXY(0);
}
private initCard(){
const card = new MySprite();
card.init(this.images.get('card'));
card.setScaleXY(this.scaleX);
card.x = this.bg.width / 2;
card.y = this.bg.height / 2;
this.bg.addChild(card);
}
private getColsePic(parent) {
const closePic = new MySprite();
closePic.init(this.images.get('close'));
closePic.scaleX = this.scaleX;
closePic.scaleY = this.scaleY;
closePic.x = parent.width;
closePic.y = 0;
parent.addChild(closePic);
return closePic;
}
setContent(data){
this.shown = false;
if(this.contentSpr){
this.bg.removeChild(this.contentSpr);
}
this.data = data;
let mW = 960 * this.scaleX;
let mH = 540 * this.scaleY;
const pic = new MySprite();
pic.init(this.images.get(this.data.picUrl));
pic.x = this.bg.width / 2;
pic.y = this.bg.height / 2;
let scale = Math.min(mW / pic.width, mH / pic.height);
pic.setScaleXY(scale);
this.contentSpr = pic;
this.bg.addChild(pic);
}
showCard(toX, toY, callback = null){
this.shown = true;
tweenChange(this.bg, {scaleX: 1, scaleY: 1, x : toX, y : toY}, 0.3, callback);
}
hideCard(toX, toY, callback = null){
tweenChange(this.bg, { scaleX: 0, scaleY: 0, x: toX, y: toY }, 0.3, () => {
this.shown = false;
callback && callback();
});
}
}
\ No newline at end of file
import { Label, ShapeCircle, ShapeRect, ShapeRectNew } from "./Unit";
export class Letter{
data;
images;
bgColor;
lineColor;
scaleX = 1;
scaleY = 1;
bg : ShapeRectNew;
dW;
canDrag = true;
init(images, data, bgColor, scaleX = 1, scaleY = 1) {
this.images = images;
this.data = data;
this.bgColor = bgColor;
this.scaleX = scaleX;
this.scaleY = scaleY;
this.initBg();
let letter = this.initView();
let w = 40 * this.scaleX + letter.getBoundingBox().width;
let h = 90 * this.scaleY;
this.bg.setSize(w, h, 0);
letter.x = w / 2;
letter.y = h / 2;
}
initBg(){
let bg = new ShapeRectNew();
bg.fillColor = this.bgColor;
bg.setOutLine(this.lineColor, 6 * this.scaleX)
this.bg = bg;
}
initView(){
const letter = new Label();
letter.text = this.data.val;
letter.textAlign = 'center';
letter.fontSize = 64;
letter.fontName = "FUTURAB";
letter.fontColor = "#2b292e";
letter.setScaleXY(this.scaleX);
letter.refreshSize();
this.bg.addChild(letter);
return letter;
}
}
\ No newline at end of file
import { Label, MySprite, ShapeCircle, ShapeRect } from "./Unit";
export class LineSpr{
images;
scaleX = 1;
scaleY = 1;
bg : ShapeCircle;
baseWidth = 180;
baseHeight = 70;
label : Label;
init(images, scaleX = 1, scaleY = 1){
this.images = images;
this.scaleX= scaleX;
this.scaleY = scaleY;
let w = this.baseWidth * this.scaleX;
let h = this.baseHeight * this.scaleY;
this.initBg(w, h);
this.initLine();
}
private initBg(w, h){
const bg = new ShapeRect();
bg.setSize(w, h);
bg.alpha = 0;
this.bg = bg;
}
private initLine(){
const line = new MySprite();
line.init(this.images.get('line'));
line.scaleX = this.scaleX;
line.scaleY = this.scaleY;
line.y = this.bg.height;
line.x = this.bg.width / 2;
this.bg.addChild(line);
return line;
}
showLabel(val){
if(this.label){
this.label.text = val;
}
else{
const label = new Label();
label.text = val;
label.textAlign = 'center';
label.fontSize = 60;
label.fontName = "MMB";
label.fontColor = "#2f2d32";
label.setScaleXY(this.scaleX);
label.refreshSize();
label.visible = false;
this.label = label;
this.bg.addChild(label);
}
this.label.visible = true;
this.label.refreshSize();
this.label.x = this.bg.width / 2;
this.label.y = this.bg.height / 2;
}
hideLabel(){
this.label.visible = false;
this.label.text = "";
}
}
\ 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, playPic = 'play', stopPic = 'stop') {
const bg = new ShapeRect();
const btnPlay = new MySprite();
btnPlay.init(this.images.get(playPic));
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(stopPic));
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){
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){
if(!this.audio){
if(callback){
callback();
}
return;
}
this.btnPlay.alpha = 0;
this.btnStop.alpha = 1;
playAudio(this.audio, false, callback);
this.playing = true;
return this.audio;
}
pauseAudio(callback = null){
this.btnPlay.alpha = 1;
this.btnStop.alpha = 0;
this.playing = false;
pauseAudio(this.audio, false, callback)
}
}
\ 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 = "tt0142m";
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)
}
}
......@@ -825,49 +825,87 @@ export class RichText extends Label {
disH = 30;
offW = 10;
maxWidth;
constructor(ctx?: any) {
super(ctx);
// this.dataArr = dataArr;
}
private getContent() {
const selfW = this.maxWidth;
drawText() {
const chr = this.text.split(' ');
let temp = '';
const row = [];
const w = selfW - this.offW * 2;
if (!this.text) {
return;
for (const c of chr) {
if (this.ctx.measureText(temp).width < w && this.ctx.measureText(temp + (c)).width <= w) {
temp += ' ' + c;
} else {
row.push(temp);
temp = ' ' + c;
}
}
row.push(temp);
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;
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 = this.ctx.measureText(row[b]).width;
if (tW > mW) {
mW = tW;
}
x = 0;
}
this._width = mW;
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;
}
this.ctx.restore();
}
drawText() {
if (!this.text) {
return;
}
row.push(temp);
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 disH = (this.fontSize + this.disH) * this.scaleY;
const x = 0;
const y = -row.length * disH / 2;
......
......@@ -17,3 +17,38 @@
src: url("../../assets/font/BRLNSDB.TTF") ;
}
@font-face
{
font-family: 'tt0142m';
src: url("../../assets/font/tt0142m.ttf") ;
}
@font-face
{
font-family: 'GOTHICB';
src: url("../../assets/font/GOTHICB.TTF") ;
}
@font-face
{
font-family: 'GOTHIC';
src: url("../../assets/font/GOTHIC.TTF") ;
}
@font-face
{
font-family: 'MMB';
src: url("../../assets/font/MMTextBook-Bold.otf") ;
}
@font-face
{
font-family: 'FUTURAB';
src: url("../../assets/font/FUTURAB.TTF") ;
}
@font-face
{
font-family: 'TCB';
src: url("../../assets/font/TCB.TTF") ;
}
import {Component, ElementRef, ViewChild, OnInit, Input, OnDestroy, HostListener} from '@angular/core';
import { Component, ElementRef, ViewChild, OnInit, Input, OnDestroy, HostListener } from '@angular/core';
import {
Label,
MySprite, tweenChange,
MySprite, randomSortByArr, RichText, ShapeRect, tweenChange,
} 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 { Title } from './Title';
import { Listening } from './Listening';
import { getDefaultTile } from '../bean/TitleBean';
import { getDefaultExercises } from '../bean/Exercises';
import { LineSpr } from './LineSpr';
import { Letter } from './Letter';
import { BigCard } from './BigCard';
......@@ -22,8 +29,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 +64,36 @@ export class PlayComponent implements OnInit, OnDestroy {
canvasLeft;
canvasTop;
saveKey = 'test_0011';
saveKey = 'YM5-16';
canTouch = true;
btnLeft;
btnRight;
pic1;
pic2;
/**标题*/
title: Title;
canTouch = true;
listening: Listening;
curAudio;
curIndex = 0;
curExercises;
lineSprArr = [];
answerSprArr = [];
oldPos;
itemOldPos;
curPic;
curDragItem;
picBtn;
bigCard: BigCard;
bigCardRenderArr = [];
@HostListener('window:resize', ['$event'])
onResize(event) {
......@@ -80,7 +106,7 @@ 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') {
......@@ -341,7 +367,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();
......@@ -442,10 +468,26 @@ export class PlayComponent implements OnInit, OnDestroy {
* 添加默认数据 便于无数据时的展示
*/
initDefaultData() {
if (!this.data.title) {
this.data.title = getDefaultTile();
}
if (!this.data.exercisesArr) {
this.data.exercisesArr = [];
}
let len = this.data.exercisesArr.length;
for (let i = len; i < 1; ++i) {
this.data.exercisesArr.push(getDefaultExercises());
}
len = this.data.exercisesArr.length;
if (!this.data.pic_url) {
this.data.pic_url = 'assets/play/default/pic.jpg';
this.data.pic_url_2 = 'assets/play/default/pic.jpg';
for (let i = 0; i < len; ++i) {
if (!this.data.exercisesArr[i]) {
this.data.exercisesArr[i] = getDefaultExercises();
}
}
}
......@@ -455,8 +497,15 @@ export class PlayComponent implements OnInit, OnDestroy {
*/
initImg() {
this.addUrlToImages(this.data.pic_url);
this.addUrlToImages(this.data.pic_url_2);
//练习题音效
for (let i = 0; i < this.data.exercisesArr.length; ++i) {
let exercises = this.data.exercisesArr[i];
if (exercises && exercises.picUrl) {
this.addUrlToImages(exercises.picUrl);
}
}
}
......@@ -465,12 +514,36 @@ export class PlayComponent implements OnInit, OnDestroy {
*/
initAudio() {
// 音频资源
this.addUrlToAudioObj(this.data.audio_url);
this.addUrlToAudioObj(this.data.audio_url_2);
this.rawAudios.forEach((item, key) => {
// 音效
this.addUrlToAudioObj(key, item);
})
// 音效
this.addUrlToAudioObj('click', this.rawAudios.get('click'), 0.3);
//标题发音
if (this.data.title.audioUrl) {
this.addUrlToAudioObj('titleAudio', this.data.title.audioUrl);
}
//练习题音效
for (let i = 0; i < this.data.exercisesArr.length; ++i) {
let exercises = this.data.exercisesArr[i];
if (exercises && exercises.audioUrl) {
this.addUrlToAudioObj(exercises.audioUrl);
}
let answerArr = exercises.answerArr;
for(let j = 0; j < answerArr.length; ++ j){
if (answerArr[j].audioUrl) {
this.addUrlToAudioObj(answerArr[j].audioUrl);
}
}
}
}
......@@ -492,8 +565,17 @@ export class PlayComponent implements OnInit, OnDestroy {
this.renderArr = [];
if (this.curAudio) {
this.curAudio.pause();
}
this.curAudio = null;
this.curIndex = 0;
this.curExercises = null;
this.bigCardRenderArr = [];
}
......@@ -504,129 +586,242 @@ export class PlayComponent implements OnInit, OnDestroy {
initView() {
this.initPic();
this.initBg();
this.initBottomPart();
this.initTitle();
this.initPicBtn();
this.showNextExerises();
}
initBottomPart() {
private initBg() {
const bg = new ShapeRect();
bg.fillColor = "#fff4c3";
bg.setSize(this.canvasWidth, this.canvasHeight);
this.renderArr.push(bg);
}
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;
private initBottomPart() {
btnLeft.setScaleXY(this.mapScale);
const audio = this.audioObj[this.curExercises.audioUrl];
this.renderArr.push(btnLeft);
if (this.listening) {
this.listening.audio = audio;
}
else {
const listening = new Listening(audio, this.images);
this.btnLeft = btnLeft;
listening.init(this.mapScale, this.mapScale);
listening.bg.x = this.canvasWidth - listening.bg.width - 10 * this.mapScale;
listening.bg.y = this.canvasHeight - listening.bg.height - 8 * this.mapScale;
this.listening = listening;
this.renderArr.push(listening.bg);
}
}
private 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;
private initPicBtn() {
const picBtn = new MySprite();
picBtn.init(this.images.get('pic-btn'));
picBtn.setScaleXY(this.mapScale);
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);
picBtn.x = this.canvasWidth - 17 * this.mapScale - picBtn.getBoundingBox().width / 2;
picBtn.y = this.canvasHeight / 2 - 190 * this.mapScale + picBtn.getBoundingBox().height / 2;
this.picBtn = picBtn;
this.renderArr.push(picBtn);
}
this.renderArr.push(pic1);
this.pic1 = pic1;
private showNextExerises() {
this.curExercises = this.data.exercisesArr[this.curIndex];
const label1 = new Label();
label1.text = this.data.text;
label1.textAlign = 'center';
label1.fontSize = 50;
label1.fontName = 'BRLNSDB';
label1.fontColor = '#ffffff';
this.initExerises(this.curExercises);
pic1.addChild(label1);
this.curIndex++;
}
private initExerises(data) {
this.initBottomPart();
this.getLineArr(data);
const pic2 = new MySprite();
pic2.init(this.images.get(this.data.pic_url_2));
pic2.x = this.canvasWidth / 2 + this.canvasWidth;
pic2.y = this.canvasHeight / 2;
pic2.setScaleXY(maxW / pic2.width);
this.getLetter(data);
this.renderArr.push(pic2);
this.pic2 = pic2;
this.getBigCard(data);
this.curPic = pic1;
this.canTouch = true;
}
private getBigCard(data) {
btnLeftClicked() {
if (!this.bigCard) {
this.lastPage();
const bigCard = new BigCard();
bigCard.init(this.images, this.mapScale, this.mapScale);
bigCard.bg.x = this.picBtn.x;
bigCard.bg.y = this.picBtn.y;
this.bigCard = bigCard;
this.bigCardRenderArr.push(bigCard.bg);
}
this.bigCard.setContent(data);
}
btnRightClicked() {
private getLineArr(data) {
this.lineSprArr = [];
let answerArr = data.answerArr;
let w = 180 * this.mapScale;
let h = 70 * this.mapScale;
let dW = 53 * this.mapScale;
let dH = 35 * this.mapScale;
let row = Math.ceil(answerArr.length / 5);
let col = answerArr.length > 5 ? 5 : answerArr.length;
let totalWidth = col * w + (col - 1) * dW;
let startX = (this.canvasWidth - totalWidth) / 2;
let startY = this.canvasHeight / 2 + 208 * this.mapScale - h;
let tempY = startY;
for (let i = 0; i < answerArr.length; ++i) {
let lineSpr = new LineSpr();
lineSpr.init(this.images, this.mapScale, this.mapScale);
lineSpr.bg.x = startX + (i % col) * (w + dW);
lineSpr.bg.y = tempY;
this.lineSprArr.push(lineSpr);
this.nextPage();
if ((i + 1) % col == 0) {
tempY = startY + h + dH;
}
this.renderArr.push(lineSpr.bg);
}
}
lastPage() {
private getLetter(data) {
if (this.curPic == this.pic1) {
return;
this.answerSprArr = [];
let answerArr = data.answerArr;
let h = 90 * this.mapScale;
let dH = 34 * this.mapScale;
let row = Math.ceil(answerArr.length / 3);
let col = answerArr.length > 3 ? 3 : answerArr.length;
//每行宽度
let widthArr = [];
//宽度临时值
let tempWidth = 0;
for (let i = 0; i < answerArr.length; ++i) {
let bgColor = "#dbd0e7";
let lineColor = "#8d4fad";
if (i % 2 == 0) {
bgColor = "#ffdd00";
lineColor = "#cd9000";
}
let answerSpr = new Letter();
answerSpr.lineColor = lineColor;
answerSpr.init(this.images, answerArr[i], bgColor, this.mapScale, this.mapScale);
this.answerSprArr.push(answerSpr);
this.renderArr.push(answerSpr.bg);
}
this.canTouch = false;
//获取随机答案
let tempAnswerSprArr = randomSortByArr(this.answerSprArr);
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;
});
}
for (let i = 0; i < tempAnswerSprArr.length; ++i) {
nextPage() {
let answerSpr = tempAnswerSprArr[i];
if (this.curPic == this.pic2) {
return;
let dW = (45 + Math.ceil(Math.random() * 60)) * this.mapScale;
answerSpr.dW = dW;
tempWidth += answerSpr.bg.width;
if ((i + 1) % col == 0) {
widthArr.push(tempWidth);
tempWidth = 0;
}
else {
tempWidth += dW;
}
}
this.canTouch = false;
if (tempWidth > 0) {
widthArr.push(tempWidth);
}
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;
});
}
let startY = this.canvasHeight / 2 - 244 * this.mapScale;
pic1Clicked() {
this.playAudio(this.data.audio_url);
}
let tempY = startY;
pic2Clicked() {
this.playAudio(this.data.audio_url_2);
}
let rowIndex = 0;
let startX = (this.canvasWidth - widthArr[rowIndex]) / 2;
for (let i = 0; i < tempAnswerSprArr.length; ++i) {
let answerSpr = tempAnswerSprArr[i];
answerSpr.bg.x = startX;
answerSpr.bg.y = tempY;
answerSpr.initX = startX;
answerSpr.initY = tempY;
if ((i + 1) % col == 0) {
tempY += h + dH;
rowIndex++;
startX = (this.canvasWidth - widthArr[rowIndex]) / 2;
}
else {
startX += answerSpr.dW + answerSpr.bg.width;
}
}
}
......@@ -636,34 +831,122 @@ export class PlayComponent implements OnInit, OnDestroy {
return;
}
if ( this.checkClickTarget(this.btnLeft) ) {
this.btnLeftClicked();
this.oldPos = { x: this.mx, y: this.my };
if (this.curAudio) {
if (!this.listening || this.curAudio != this.listening.audio) {
this.curAudio.pause();
}
}
//如果大卡片显示,则阻止后续点击事件
if (this.bigCard.shown) {
this.clickBigCard();
return;
}
if ( this.checkClickTarget(this.btnRight) ) {
this.btnRightClicked();
if (this.listening && this.checkClickTarget(this.listening.bg)) {
this.curAudio = this.listening.play(() => {
this.listening.reset();
});
return;
}
if ( this.checkClickTarget(this.pic1) ) {
this.pic1Clicked();
if (this.curAudio) {
if (this.listening && this.curAudio == this.listening.audio) {
this.listening.pauseAudio();
}
else {
this.curAudio.pause();
}
}
if (this.checkClickTarget(this.title.titleBg)) {
this.curAudio = this.title.playAudio();
return;
}
if ( this.checkClickTarget(this.pic2) ) {
this.pic2Clicked();
if (this.checkClickTarget(this.picBtn)) {
this.clickPicBtn();
return;
}
let clicked = false;
let target;
let callback;
for (let i = 0; i < this.answerSprArr.length; ++i) {
let answerSpr = this.answerSprArr[i];
if (!answerSpr.canDrag) {
continue;
}
if (this.checkClickTarget(answerSpr.bg)) {
callback = this.clickLetter.bind(this);
clicked = true;
target = answerSpr;
break;
}
}
if (clicked) {
callback(target);
return;
}
}
mapMove(event) {
if (!this.curDragItem) {
return;
}
if (!this.isMove(this.curDragItem)) {
return;
}
const addX = this.mx - this.oldPos.x;
const addY = this.my - this.oldPos.y;
this.curDragItem.bg.x += addX;
this.curDragItem.bg.y += addY;
this.curDragItem.bg.visible = true;
let lineIndex = this.curDragItem.lineIndex;
if(lineIndex && lineIndex != -1){
let lineSpr = this.lineSprArr[lineIndex];
lineSpr.hideLabel();
lineSpr.answered = false;
this.curDragItem.lineIndex = -1;
}
this.oldPos = { x: this.mx, y: this.my };
}
mapUp(event) {
if (!this.curDragItem) {
return;
}
if (!this.isMove(this.curDragItem)) {
this.curAudio = this.playAudio(this.curDragItem.data.audioUrl, true);
this.curDragItem = null;
}
else {
this.checkLetter();
}
}
......@@ -682,9 +965,256 @@ export class PlayComponent implements OnInit, OnDestroy {
this.updateArr(this.renderArr);
this.updateArr(this.bigCardRenderArr);
}
private clickPicBtn() {
this.canTouch = false;
let toX = (this.canvasWidth - this.bigCard.bg.width) / 2;
let toY = (this.canvasHeight - this.bigCard.bg.height) / 2;
this.playAudio('open');
this.bigCard.showCard(toX, toY, () => {
this.canTouch = true;
})
}
private clickBigCard() {
if (this.checkClickTarget(this.bigCard.closePic)) {
this.playAudio('close');
this.canTouch = false;
let toX = this.picBtn.x;
let toY = this.picBtn.y;
this.bigCard.hideCard(toX, toY, () => {
this.canTouch = true;
})
return;
}
}
isMove(item) {
let result = false;
if (!item) {
return result;
}
let x = this.itemOldPos.x;
let y = this.itemOldPos.y;
if (Math.abs(x - this.mx) > 10 * this.mapScale || Math.abs(y - this.my) > 10 * this.mapScale) {
result = true;
}
return result;
}
/**
* 点击字母
* @param item
*/
clickLetter(item: any) {
if (!this.checkClickTarget(item.bg)) {
return;
}
this.itemOldPos = { x: this.mx, y: this.my };
this.curDragItem = item;
let index = this.renderArr.indexOf(item.bg);
if (index != -1) {
this.renderArr.splice(index, 1);
}
this.renderArr.push(item.bg);
}
/**
* 检查拖拽单词是否匹配
*/
checkLetter() {
if (!this.curDragItem) {
return;
}
let letterItem = this.curDragItem;
this.curDragItem = null;
this.canTouch = false;
letterItem.lineIndex = -1;
const back = () => {
this.playAudio('back', true);
let bg = letterItem.bg;
let toX = letterItem.initX;
let toY = letterItem.initY;
letterItem.lineIndex = -1;
tweenChange(bg, { x: toX, y: toY }, 0.3, () => {
this.canTouch = true;
})
}
let lineSpr;
let lineIndex = 0;
for (let i = 0; i < this.lineSprArr.length; ++i) {
let tLineSpr = this.lineSprArr[i];
if (this.checkPointInRect(this.mx, this.my, tLineSpr.bg.getBoundingBox())) {
lineIndex = i;
lineSpr = tLineSpr;
break;
}
}
if (!lineSpr || lineSpr.answered) {
back();
return;
}
this.playAudio('right', true);
lineSpr.answered = true;
letterItem.bg.x = lineSpr.bg.x + (letterItem.bg.width - letterItem.bg.width) / 2;
letterItem.bg.y = lineSpr.bg.y + (letterItem.bg.height - letterItem.bg.height) / 2;
letterItem.bg.visible = false;
letterItem.lineIndex = lineIndex;
let val = letterItem.data.val;
lineSpr.showLabel(val);
lineSpr.letterItem = letterItem;
let allAnswered = true;
for (let i = 0; i < this.lineSprArr.length; ++i) {
if (!this.lineSprArr[i].answered) {
allAnswered = false;
break;
}
}
//如果全部答完,判断是否正确
if (!allAnswered) {
this.canTouch = true;
return;
}
this.isRight();
}
private isRight() {
let isRight = true;
for (let i = 0; i < this.answerSprArr.length; ++i) {
let answerSpr = this.answerSprArr[i];
let compareSpr = this.lineSprArr[i].letterItem;
if (answerSpr != compareSpr) {
isRight = false;
break;
}
}
const back = (item) => {
let bg = item.bg;
let toX = item.initX;
let toY = item.initY;
tweenChange(bg, { x: toX, y: toY }, 0.3, () => {
this.canTouch = true;
item.canDrag = true;
})
}
//回答失败,则返回重选
if (!isRight) {
this.playAudio('back', true);
for (let i = 0; i < this.answerSprArr.length; ++i) {
let answerSpr = this.answerSprArr[i];
let lineSpr = this.lineSprArr[i];
lineSpr.hideLabel();
lineSpr.answered = false;
answerSpr.bg.visible = true;
back(answerSpr);
}
return;
}
this.showRight();
}
private showRight() {
for(let i = 0; i < this.lineSprArr.length; ++ i){
let lineSpr = this.lineSprArr[i];
lineSpr.bg.visible = false;
}
let mW = 800 * this.mapScale;
let mH = 397 * this.mapScale;
const pic = new MySprite();
pic.init(this.images.get(this.curExercises.picUrl));
pic.x = this.canvasWidth / 2;
pic.y = this.canvasHeight / 2 - 117 * this.mapScale;
let scale = Math.min(mW / pic.width, mH / pic.height);
pic.setScaleXY(scale);
this.renderArr.push(pic);
let richText = new RichText();
richText.fontColor = '#2b292e';
richText.fontName = 'MMB';
richText.text = this.curExercises.val;
richText.fontSize = 60;
richText.textAlign = 'left';
richText.setScaleXY(this.mapScale);
richText.maxWidth = 1120;
richText.refreshSize();
richText.x = ( this.canvasWidth - richText.getBoundingBox().width) / 2;
richText.y = this.canvasHeight + 150 * this.mapScale;
this.renderArr.push(richText);
let toY = this.canvasHeight / 2 + 150 * this.mapScale;
tweenChange(richText, {y : toY}, 0.5, () =>{
this.canTouch = true;
});
}
}
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"],
['close', "assets/play/close.png"],
['pic-btn', "assets/play/pic-btn.png"],
['line', "assets/play/line.png"],
['card', "assets/play/card.png"],
];
......@@ -12,7 +13,11 @@ const res = [
const resAudio = [
['click', "assets/play/music/click.mp3"],
['right', "assets/play/music/right.mp3"],
['right1', "assets/play/music/right.mp3"],
['back', "assets/play/music/back.mp3"],
['open', "assets/play/music/open.mp3"],
['close', "assets/play/music/close.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