Commit 527458ca authored by liujiaxin's avatar liujiaxin

1

parent 8a51c964
...@@ -30,7 +30,10 @@ ...@@ -30,7 +30,10 @@
], ],
"styles": [ "styles": [
"./node_modules/ng-zorro-antd/ng-zorro-antd.min.css", "./node_modules/ng-zorro-antd/ng-zorro-antd.min.css",
"src/styles.css" "./node_modules/font-awesome/css/font-awesome.css",
"./node_modules/bootstrap/dist/css/bootstrap.min.css",
"./node_modules/animate.css/animate.min.css",
"src/styles.scss"
], ],
"scripts": [] "scripts": []
}, },
......
@import '../style/common_mixin.css'; .dynamic-delete-button{
font-size: 2rem;
cursor: pointer;
.model-content { }
width: 100%; .card-word {
height: 100%; margin-top: 1rem;
} }
<form nz-form style="width:100%">
<div class="model-content"> <nz-form-item
style="width: 25%;float: left;padding: 0 .5rem"
*ngFor="let control of cardsArray;let i = index">
<div style="position: absolute; left: 200px; top: 100px; width: 800px;"> <nz-form-control [nzXs]="24" [nzSm]="24" [nzOffset]="0">
<div nz-col class="gutter-row" [nzSpan]="24">
<input type="text" nz-input [(ngModel)]="item.text" (blur)="save()"> <app-upload-image-with-preview
[picUrl]="control.pic_id ? control.pic_id : null"
<app-upload-image-with-preview (imageUploaded)="onImageUploadSuccess(control, $event)"
[picUrl]="item.pic_url" ></app-upload-image-with-preview>
(imageUploaded)="onImageUploadSuccess($event, 'pic_url')" <i class="anticon anticon-close dynamic-delete-button"
></app-upload-image-with-preview> (click)="removeCard(control, $event, i)"
style="position: absolute;right: .5rem;top: .5rem;"></i>
<app-audio-recorder </div>
[audioUrl]="item.audio_url" <!--<div *ngIf="cardsArray.length>1" style="position: absolute;right: .5rem;top: .5rem;">-->
(audioUploaded)="onAudioUploadSuccess($event, 'audio_url')" <!--<i class="anticon anticon-close dynamic-delete-button"-->
></app-audio-recorder> <!--(click)="removeCard(control, $event, i)"></i>-->
<app-custom-hot-zone></app-custom-hot-zone> <!--</div>-->
<app-upload-video></app-upload-video> <input nz-input placeholder="word" class="card-word"
<app-lesson-title-config></app-lesson-title-config> (blur)="addCardWord(control, $event, i)"
</div> [value]="control.word"
nzSize="large">
</nz-form-control>
</div> </nz-form-item>
<nz-form-item style="margin-top: 1rem;clear: both">
\ No newline at end of file <nz-form-control
[nzSpan]="24"
nzValidateStatus="error"
[ngStyle]="{'visibility': hasError ? 'visible' : 'hidden'}"
[nzXs]="{span:24,offset:0}"
[nzSm]="{span:20,offset:4}">
<nz-form-explain>image and word can not be empty</nz-form-explain>
</nz-form-control>
</nz-form-item>
<nz-form-item style="margin-top: 1rem;clear: both">
<nz-form-control [nzXs]="{span:24,offset:0}" [nzSm]="{span:20,offset:4}">
<button nz-button nzType="dashed" style="width:60%" (click)="addCard($event)">
<i class="anticon anticon-plus"></i> Add Card</button>
</nz-form-control>
</nz-form-item>
</form>
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';
...@@ -10,25 +11,43 @@ import {Component, EventEmitter, Input, OnDestroy, OnChanges, OnInit, Output, Ap ...@@ -10,25 +11,43 @@ import {Component, EventEmitter, Input, OnDestroy, OnChanges, OnInit, Output, Ap
export class FormComponent implements OnInit, OnChanges, OnDestroy { export class FormComponent implements OnInit, OnChanges, OnDestroy {
// 储存数据用 // 储存数据用
saveKey = "test_0011"; saveKey = 'ww_matching_cards';
// 储存对象 // 储存对象
item; item;
hasError = false;
cardsArray: Array<{
pic_id: string,
word: string
}>;
constructor(private appRef: ApplicationRef,private changeDetectorRef: ChangeDetectorRef) { constructor(private appRef: ApplicationRef,
private nzMessageService: NzMessageService,
private changeDetectorRef: ChangeDetectorRef) {
} }
ngOnInit() { ngOnInit() {
this.item = {}; this.item = {
contentObj: {}
};
if (this.item.contentObj.cards) {
this.cardsArray = this.item.contentObj.cards;
} else {
this.cardsArray = [];
}
// 获取存储的数据 // 获取存储的数据
(<any> window).courseware.getData((data) => { (window as any).courseware.getData((data) => {
if (data) { if (data) {
this.item = data; this.item = data;
if (this.item.contentObj.cards) {
this.cardsArray = this.item.contentObj.cards;
} else {
this.cardsArray = [];
}
} }
this.init(); this.init();
...@@ -54,25 +73,24 @@ export class FormComponent implements OnInit, OnChanges, OnDestroy { ...@@ -54,25 +73,24 @@ export class FormComponent implements OnInit, OnChanges, OnDestroy {
} }
/** // /**
* 储存图片数据 // * 储存图片数据
* @param e // * @param e
*/ // */
onImageUploadSuccess(e, key) { // onImageUploadSuccess(e, key) {
//
this.item[key] = e.url; // this.item[key] = e.url;
this.save(); // this.save();
} // }
// /**
/** // * 储存音频数据
* 储存音频数据 // * @param e
* @param e // */
*/ // onAudioUploadSuccess(e, key) {
onAudioUploadSuccess(e, key) { //
// this.item[key] = e.url;
this.item[key] = e.url; // this.save();
this.save(); // }
}
...@@ -80,7 +98,7 @@ export class FormComponent implements OnInit, OnChanges, OnDestroy { ...@@ -80,7 +98,7 @@ export class FormComponent implements OnInit, OnChanges, OnDestroy {
* 储存数据 * 储存数据
*/ */
save() { save() {
(<any> window).courseware.setData(this.item, null, this.saveKey); (window as any).courseware.setData(this.item, null, this.saveKey);
this.refresh(); this.refresh();
} }
...@@ -92,6 +110,50 @@ export class FormComponent implements OnInit, OnChanges, OnDestroy { ...@@ -92,6 +110,50 @@ export class FormComponent implements OnInit, OnChanges, OnDestroy {
this.appRef.tick(); this.appRef.tick();
}, 1); }, 1);
} }
checkValidSts() {
for (const item of this.cardsArray) {
if (!item.pic_id || !(item.word && item.word.trim())) {
return true;
}
}
return false;
}
addCard(evt) {
this.hasError = this.checkValidSts();
if (this.hasError) {
return;
}
if (this.cardsArray.length >= 8 ) {
this.nzMessageService.info('you can only add eight item!');
return;
}
this.cardsArray.push({pic_id: '', word: ''});
}
removeCard(control, evt , i) {
this.cardsArray.splice(i, 1);
this.item.contentObj.cards = this.cardsArray;
this.save();
}
addCardWord(control, evt, i) {
const val = evt.target.value.trim();
control.word = val;
if (control.word && control.pic_id) {
this.item.contentObj.cards = this.cardsArray;
this.save();
this.hasError = false;
} else {
this.hasError = true;
}
}
onImageUploadSuccess(control, info) {
control.pic_id = info.url;
if (control.word && control.pic_id) {
this.item.contentObj.cards = this.cardsArray;
this.save();
this.hasError = true;
} else {
this.hasError = false;
}
}
} }
import {AfterViewInit, HostListener} from '@angular/core';
export class BaseResizeComponent implements AfterViewInit {
resizeTimer: any;
constructor() {
}
ngAfterViewInit() {
this.autoSizeText();
}
@HostListener('window:resize', ['$event'])
onResize(event) {
clearTimeout(this.resizeTimer);
this.resizeTimer = setTimeout(() => {
this.autoSizeText();
}, 500);
}
resizeText(el: any, fontSize: number = 10) {
fontSize++; // el.computedStyleMap().get('font-size');
// Ken 2018-08-09 14:39 无文字时, 不需要计算字号
if (el.textContent.length === 0) {
return;
}
el.style.fontSize = (fontSize + 2) + 'px';
if (el.scrollHeight > el.parentElement.offsetHeight) {
el.style.fontSize = fontSize + 'px';
} else {
setTimeout(() => {
this.resizeText(el, fontSize);
}, 32);
}
}
autoSizeText() {
const elements: NodeListOf<HTMLElement> = document.querySelectorAll('.fit-font-size');
for (let i = 0; i < elements.length; i++) {
const el = elements.item(i);
this.resizeText(el);
}
}
}
.game-container {
width: 100%;
height: 100%;
background: #ffffff;
background-size: cover;
}
#canvas {
}
@font-face
{
font-family: 'BRLNSDB';
src: url("../../assets/font/BRLNSDB.TTF") ;
}
<div class="game-container" #wrap> <div class="player-matching-cards">
<canvas id="canvas" #canvas></canvas> <div nz-row nzGutter="16" style="height: 100%">
<div nz-col class="gutter-row cell"
#itemContainer
[nzSpan]="24/cols"
[ngStyle]="{height: (100/rows) + '%', 'user-select': 'none'}"
*ngFor="let card of all">
<div class="item" (click)="flipCard($event, card.type, card.word)">
<div class="k-16-9">
<img class="back" src="/assets/matching-cards/cardback.png" alt="">
<div class="k-full-fill" *ngIf="card.type === 'pic'">
<img class="pic" [src]="card.pic_id">
</div>
<div class=" k-full-fill" *ngIf="card.type === 'word'">
<span class="word fit-font-size">{{card.word}}</span>
</div>
</div>
</div>
</div>
</div>
</div> </div>
.player-matching-cards {
background: #f4ffeb;
padding: 4vh 6vh;
width: 100%;
height: 100%;
.cell{
display: table;
.item{
display: table-cell;
vertical-align: middle;
}
}
.item{
.k-16-9{
cursor: pointer;
}
.k-16-9:before{
position: absolute;
top: 0;
}
.back{
width: 100%;
height: 100%;
}
.k-full-fill{
//display: none;
visibility: hidden;
padding: .25vh;
border-radius: 2vh;
border: 2px solid #c0e9c5;
//display: flex;
justify-content: center;
align-items: center;
background: #fff;
.word{
width: 100%;
//padding: 1rem;
display: block;
font-size: 4vh;
font-weight: 600;
overflow: hidden;
text-align: center;
font-family: 'ChalkboardSE-Regular';
max-width: 100%;
max-height: 100%;
word-wrap: break-word;
word-break: break-word;
overflow: hidden;
//box-sizing: border-box;
//height: 100%;
//max-width: 100%;
//max-height: 100%;
}
img.pic {
position: absolute;
top: 50%;
left: 50%;
transform: translateX(-50%) translateY(-50%);
width: auto;
height: 100%;
}
}
}
.item.flip-out{
animation-name: open_card_step_1;
animation-duration: .4s;
animation-iteration-count: 1;
animation-timing-function: linear;
animation-fill-mode: forwards;
}
.item.flip-in{
animation-name: open_card_step_2;
animation-duration: .4s;
animation-iteration-count: 1;
animation-timing-function: linear;
animation-fill-mode: forwards;
}
@keyframes open_card_step_1 {
0% {transform: scaleX(1)}
100% {transform: scaleX(0) }
}
@keyframes open_card_step_2 {
0% {transform: scaleX(0)}
100% {transform: scaleX(1) }
}
}
import {Component, ElementRef, ViewChild, OnInit, Input, OnDestroy, HostListener} from '@angular/core'; import { Component, ElementRef, ViewChild, OnInit, Input, OnDestroy, HostListener, OnChanges } from '@angular/core';
import { import * as _ from 'lodash';
Label,
MySprite, tweenChange,
} from './Unit'; import { BaseResizeComponent } from './base-resize.component';
import {res, resAudio} from './resources';
import {Subject} from 'rxjs';
import {debounceTime} from 'rxjs/operators';
import TWEEN from '@tweenjs/tween.js';
...@@ -18,660 +10,266 @@ import TWEEN from '@tweenjs/tween.js'; ...@@ -18,660 +10,266 @@ import TWEEN from '@tweenjs/tween.js';
@Component({ @Component({
selector: 'app-play', selector: 'app-play',
templateUrl: './play.component.html', templateUrl: './play.component.html',
styleUrls: ['./play.component.css'] styleUrls: ['./play.component.scss']
}) })
export class PlayComponent implements OnInit, OnDestroy { export class PlayComponent extends BaseResizeComponent implements OnInit {
saveKey = 'ww_matching_cards';
@ViewChild('canvas', {static: true }) canvas: ElementRef; @ViewChild('canvas', {static: true }) canvas: ElementRef;
@ViewChild('wrap', {static: true }) wrap: ElementRef; @ViewChild('wrap', {static: true }) wrap: ElementRef;
// 数据 // 数据
data; data;
@ViewChild('itemContainer')
ctx; itemContainer: ElementRef;
cardsArray: Array<{
canvasWidth = 1280; // canvas实际宽度 pic_id: number,
canvasHeight = 720; // canvas实际高度 word: string
}>;
canvasBaseW = 1280; // canvas 资源预设宽度 cols = 1;
canvasBaseH = 720; // canvas 资源预设高度 rows = 0;
all = [];
mx; // 点击x坐标
my; // 点击y坐标 pairNode = [];
ANIMATING = false;
// 资源 rightAudio = new Audio();
rawImages = new Map(res); wrongAudio = new Audio();
rawAudios = new Map(resAudio);
constructor() {
images = new Map(); super();
this.rightAudio.src = '/assets/right.mp3';
animationId: any; this.rightAudio.load();
winResizeEventStream = new Subject(); this.wrongAudio.src = '/assets/wrong.mp3';
this.wrongAudio.load();
audioObj = {};
renderArr;
mapScale = 1;
canvasLeft;
canvasTop;
saveKey = 'test_0011';
btnLeft;
btnRight;
pic1;
pic2;
canTouch = true;
curPic;
@HostListener('window:resize', ['$event'])
onResize(event) {
this.winResizeEventStream.next();
} }
ngOnInit() { ngOnInit() {
this.data = {}; this.data = {};
// 获取数据 // 获取数据
const getData = (<any> window).courseware.getData; const getData = ( window as any).courseware.getData;
getData((data) => { getData((data) => {
if (data && typeof data == 'object') { if (data && typeof data === 'object') {
this.data = data; this.data = data;
} }
// console.log('data:' , data); this.dataChange();
// 初始化 各事件监听
this.initListener();
// 若无数据 则为预览模式 需要填充一些默认数据用来显示
this.initDefaultData();
// 初始化 音频资源
this.initAudio();
// 初始化 图片资源
this.initImg();
// 开始预加载资源
this.load();
}, this.saveKey);
}
ngOnDestroy() {
window['curCtx'] = null;
window.cancelAnimationFrame(this.animationId);
}
load() {
// 预加载资源
this.loadResources().then(() => {
window["air"].hideAirClassLoading(this.saveKey, this.data); window["air"].hideAirClassLoading(this.saveKey, this.data);
this.init(); }, this.saveKey);
this.update();
});
}
init() {
this.initCtx();
this.initData();
this.initView();
}
initCtx() {
this.canvasWidth = this.wrap.nativeElement.clientWidth;
this.canvasHeight = this.wrap.nativeElement.clientHeight;
this.canvas.nativeElement.width = this.wrap.nativeElement.clientWidth;
this.canvas.nativeElement.height = this.wrap.nativeElement.clientHeight;
this.ctx = this.canvas.nativeElement.getContext('2d');
this.canvas.nativeElement.width = this.canvasWidth;
this.canvas.nativeElement.height = this.canvasHeight;
window['curCtx'] = this.ctx;
}
updateItem(item) {
if (item) {
item.update();
}
}
updateArr(arr) {
if (!arr) {
return;
}
for (let i = 0; i < arr.length; i++) {
arr[i].update(this);
}
}
initListener() {
this.winResizeEventStream
.pipe(debounceTime(500))
.subscribe(data => {
this.renderAfterResize();
});
// ---------------------------------------------
const setParentOffset = () => {
const rect = this.canvas.nativeElement.getBoundingClientRect();
this.canvasLeft = rect.left;
this.canvasTop = rect.top;
};
const setMxMyByTouch = (event) => {
if (event.touches.length <= 0) {
return;
}
if (this.canvasLeft == null) {
setParentOffset();
}
this.mx = event.touches[0].pageX - this.canvasLeft;
this.my = event.touches[0].pageY - this.canvasTop;
};
const setMxMyByMouse = (event) => {
this.mx = event.offsetX;
this.my = event.offsetY;
};
// ---------------------------------------------
let firstTouch = true;
const touchDownFunc = (e) => {
if (firstTouch) {
firstTouch = false;
removeMouseListener();
}
setMxMyByTouch(e);
this.mapDown(e);
};
const touchMoveFunc = (e) => {
setMxMyByTouch(e);
this.mapMove(e);
};
const touchUpFunc = (e) => {
setMxMyByTouch(e);
this.mapUp(e);
};
const mouseDownFunc = (e) => {
if (firstTouch) {
firstTouch = false;
removeTouchListener();
}
setMxMyByMouse(e);
this.mapDown(e);
};
const mouseMoveFunc = (e) => {
setMxMyByMouse(e);
this.mapMove(e);
};
const mouseUpFunc = (e) => {
setMxMyByMouse(e);
this.mapUp(e);
};
const element = this.canvas.nativeElement;
const addTouchListener = () => {
element.addEventListener('touchstart', touchDownFunc);
element.addEventListener('touchmove', touchMoveFunc);
element.addEventListener('touchend', touchUpFunc);
element.addEventListener('touchcancel', touchUpFunc);
};
const removeTouchListener = () => {
element.removeEventListener('touchstart', touchDownFunc);
element.removeEventListener('touchmove', touchMoveFunc);
element.removeEventListener('touchend', touchUpFunc);
element.removeEventListener('touchcancel', touchUpFunc);
};
const addMouseListener = () => {
element.addEventListener('mousedown', mouseDownFunc);
element.addEventListener('mousemove', mouseMoveFunc);
element.addEventListener('mouseup', mouseUpFunc);
};
const removeMouseListener = () => {
element.removeEventListener('mousedown', mouseDownFunc);
element.removeEventListener('mousemove', mouseMoveFunc);
element.removeEventListener('mouseup', mouseUpFunc);
};
addMouseListener();
addTouchListener();
}
playAudio(key, now = false, callback = null) {
const audio = this.audioObj[key];
if (audio) {
if (now) {
audio.pause();
audio.currentTime = 0;
}
if (callback) {
audio.onended = () => {
callback();
};
}
audio.play();
}
}
loadResources() {
const pr = [];
this.rawImages.forEach((value, key) => {// 预加载图片
const p = this.preload(value)
.then(img => {
this.images.set(key, img);
})
.catch(err => console.log(err));
pr.push(p);
});
this.rawAudios.forEach((value, key) => {// 预加载音频
const a = this.preloadAudio(value)
.then(() => {
// this.images.set(key, img);
})
.catch(err => console.log(err));
pr.push(a);
});
return Promise.all(pr);
}
preload(url) {
return new Promise((resolve, reject) => {
const img = new Image();
// img.crossOrigin = "anonymous";
img.onload = () => resolve(img);
img.onerror = reject;
img.src = url;
});
} }
dataChange() {
this.cardsArray = _.get(this.data, 'contentObj.cards', []);
preloadAudio(url) { const tmp = [];
return new Promise((resolve, reject) => { this.cardsArray.map( item => {
const audio = new Audio(); tmp.push({type: 'word', word: item.word});
audio.oncanplay = (a) => { tmp.push({type: 'pic', word: item.word, pic_id: item.pic_id});
resolve();
};
audio.onerror = () => {
reject();
};
audio.src = url;
audio.load();
}); });
} this.all = this.shuffle(tmp);
if (this.all.length <= 2) {
this.cols = 2;
renderAfterResize() { this.rows = 1;
this.canvasWidth = this.wrap.nativeElement.clientWidth; } else if (this.all.length > 2 && this.all.length <= 4) {
this.canvasHeight = this.wrap.nativeElement.clientHeight; this.cols = 2;
this.init(); this.rows = 2;
} } else if (this.all.length > 4 && this.all.length <= 6) {
this.cols = 3;
this.rows = 2;
} else if (this.all.length > 6 && this.all.length <= 8) {
this.cols = 3;
this.rows = 3;
checkClickTarget(target) { } else if (this.all.length > 8 && this.all.length <= 12) {
this.cols = 4;
const rect = target.getBoundingBox(); this.rows = 3;
} else if (this.all.length > 12 && this.all.length <= 16) {
if (this.checkPointInRect(this.mx, this.my, rect)) { this.cols = 4;
return true; this.rows = 4;
} }
return false;
} }
getWorlRect(target) { shuffle(array) {
array = array.concat();
let rect = target.getBoundingBox(); let i = 0
, j = 0
, temp = null;
if (target.parent) { for (i = array.length - 1; i > 0; i -= 1) {
j = Math.floor(Math.random() * (i + 1));
const pRect = this.getWorlRect(target.parent); temp = array[i];
rect.x += pRect.x; array[i] = array[j];
rect.y += pRect.y; array[j] = temp;
} }
return rect; return array;
} }
toggleCardOpenClose(item) {
checkPointInRect(x, y, rect) { if (!item.opened && item.classList.contains('flip-out')) {
if (x >= rect.x && x <= rect.x + rect.width) { // console.log('open card', item)
if (y >= rect.y && y <= rect.y + rect.height) { item.querySelector('.back').style.visibility = 'hidden';
return true; item.querySelector('.k-full-fill').style.display = 'flex';
item.querySelector('.k-full-fill').style.visibility = 'visible';
item.classList.remove('flip-out');
item.classList.add('flip-in');
item.opened = 1;
} else if (item.opened === 1 && item.classList.contains('flip-out')) {
// console.log('close card', item)
item.querySelector('.back').style.visibility = 'visible';
item.querySelector('.k-full-fill').style.display = 'none';
this.removeNodeFromPair(item);
item.classList.remove('flip-out');
item.classList.add('flip-in');
item.opened = 0;
} else if (item.classList.contains('flip-in')) {
// console.log('fliping end', item)
item.fliping = false;
// console.log(' ````````````````````', this.pairNode.length);
if (this.pairNode.length === 2) {
// console.log('current open two card', item)
if (this.pairNode[0].word === this.pairNode[1].word &&
this.pairNode[0].type !== this.pairNode[1].type) {
const a = this.pairNode[0];
const b = this.pairNode[1];
// console.log('card match right', [a, b])
if (a && b) {
this.rightAudio.pause();
this.rightAudio.currentTime = 0;
setTimeout(() => {
this.rightAudio.play();
});
}
// console.log('~~~~~~~~~~~~~~~~~~~~~~~~~~', this.pairNode.length)
setTimeout(() => {
// try {
// console.log(123123, [a, b], this.ANIMATING)
if (a) {
// console.log('hide a', a);
// a.style.visibility = 'hidden';
this.removeNodeFromPair(a , true);
}
if (b) {
// console.log('hide b', b);
// b.style.visibility = 'hidden';
this.removeNodeFromPair(b , true);
}
// } catch (e) {
// debugger
// }
// item.style.visibility = 'hidden';
// console.log('hide', item);
// this.pairNode = [];
// item.action = 'close'
// this.removeNodeFromPair(item, true);
}, 500);
} else {
// console.log('two cards not match', item)
// console.log(1111, this.pairNode[0].opened, this.pairNode[1].opened )
if (this.pairNode.length && this.pairNode[0].opened && this.pairNode[1].opened) {
this.wrongAudio.pause();
this.wrongAudio.currentTime = 0;
setTimeout(() => {
this.wrongAudio.play();
});
// console.log('close not match cards', item)
const a = this.pairNode[0];
const b = this.pairNode[1];
setTimeout(() => {
// console.log('wrong close card', a.opened, b.opened, this.pairNode.length);
if (a.opened) {
this.doFlipCard(a, 'close');
}
if (b.opened) {
this.doFlipCard(b, 'close');
}
}, 500);
}
// this.pairNode = [];
this.removeNodeFromPair(item);
}
} else {
// console.log('card opened not pair', this.ANIMATING, item)
this.removeNodeFromPair(item);
} }
} }
return false;
} }
removeNodeFromPair(item , force = false /*item, force?: bool*/) {
// console.log(item, item.action)
if (force || item.action === 'close') {
const idx = this.pairNode.indexOf(item);
if (idx > -1) {
addUrlToAudioObj(key, url = null, vlomue = 1, loop = false, callback = null) { this.pairNode.splice(idx, 1);
}
const audioObj = this.audioObj; if (this.pairNode.length === 0) {
// console.log('set animating to false')
if (url == null) { this.ANIMATING = false;
url = key; }
}
this.rawAudios.set(key, url);
const audio = new Audio();
audio.src = url;
audio.load();
audio.loop = loop;
audio.volume = vlomue;
audioObj[key] = audio;
if (callback) {
audio.onended = () => {
callback();
};
} }
} }
doFlipCard(item, act) {
addUrlToImages(url) { item.classList.remove('flip-in', 'flip-out');
this.rawImages.set(url, url); if (!item.hasListener) {
} item.addEventListener('animationend', () => {
this.toggleCardOpenClose(item);
}, false);
item.hasListener = true;
// ======================================================编写区域==========================================================================
/**
* 添加默认数据 便于无数据时的展示
*/
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';
} }
} item.action = act;
// console.log(act, item.opened, item.classList);
if (act === 'open' && !item.opened) {
/** // item.opened = 1;
* 添加预加载图片 // console.log(item, item.opened);
*/ item.classList.add('flip-out');
initImg() { } else if (act === 'close' && item.opened) {
// item.opened = 0;
this.addUrlToImages(this.data.pic_url);
this.addUrlToImages(this.data.pic_url_2); item.classList.add('flip-out');
}
/**
* 添加预加载音频
*/
initAudio() {
// 音频资源
this.addUrlToAudioObj(this.data.audio_url);
this.addUrlToAudioObj(this.data.audio_url_2);
// 音效
this.addUrlToAudioObj('click', this.rawAudios.get('click'), 0.3);
}
/**
* 初始化数据
*/
initData() {
const sx = this.canvasWidth / this.canvasBaseW;
const sy = this.canvasHeight / this.canvasBaseH;
const s = Math.min(sx, sy);
this.mapScale = s;
// this.mapScale = sx;
// this.mapScale = sy;
this.renderArr = [];
}
/**
* 初始化试图
*/
initView() {
this.initPic();
this.initBottomPart();
}
initBottomPart() {
const btnLeft = new MySprite();
btnLeft.init(this.images.get('btn_left'));
btnLeft.x = this.canvasWidth - 150 * this.mapScale;
btnLeft.y = this.canvasHeight - 100 * this.mapScale;
btnLeft.setScaleXY(this.mapScale);
this.renderArr.push(btnLeft);
this.btnLeft = btnLeft;
const btnRight = new MySprite();
btnRight.init(this.images.get('btn_right'));
btnRight.x = this.canvasWidth - 50 * this.mapScale;
btnRight.y = this.canvasHeight - 100 * this.mapScale;
btnRight.setScaleXY(this.mapScale);
this.renderArr.push(btnRight);
this.btnRight = btnRight;
}
initPic() {
const maxW = this.canvasWidth * 0.7;
const pic1 = new MySprite();
pic1.init(this.images.get(this.data.pic_url));
pic1.x = this.canvasWidth / 2;
pic1.y = this.canvasHeight / 2;
pic1.setScaleXY(maxW / pic1.width);
this.renderArr.push(pic1);
this.pic1 = pic1;
const label1 = new Label();
label1.text = this.data.text;
label1.textAlign = 'center';
label1.fontSize = 50;
label1.fontName = 'BRLNSDB';
label1.fontColor = '#ffffff';
pic1.addChild(label1);
const pic2 = new MySprite();
pic2.init(this.images.get(this.data.pic_url_2));
pic2.x = this.canvasWidth / 2 + this.canvasWidth;
pic2.y = this.canvasHeight / 2;
pic2.setScaleXY(maxW / pic2.width);
this.renderArr.push(pic2);
this.pic2 = pic2;
this.curPic = pic1;
}
btnLeftClicked() {
this.lastPage();
}
btnRightClicked() {
this.nextPage();
}
lastPage() {
if (this.curPic == this.pic1) {
return;
} }
this.canTouch = false;
const moveLen = this.canvasWidth;
tweenChange(this.pic1, {x: this.pic1.x + moveLen}, 1);
tweenChange(this.pic2, {x: this.pic2.x + moveLen}, 1, () => {
this.canTouch = true;
this.curPic = this.pic1;
});
} }
nextPage() { flipCard(evt, type, word) {
// if (this.MODE !== 'IDLE' && this.MODE !== 'ONE') {
if (this.curPic == this.pic2) { // return;
// }
if (this.ANIMATING) {
return; return;
} }
this.rightAudio.pause();
this.canTouch = false; this.rightAudio.currentTime = 0;
// this.rightAudio.play();
const moveLen = this.canvasWidth; setTimeout(() => {
tweenChange(this.pic1, {x: this.pic1.x - moveLen}, 1); this.rightAudio.play();
tweenChange(this.pic2, {x: this.pic2.x - moveLen}, 1, () => {
this.canTouch = true;
this.curPic = this.pic2;
}); });
} const item = evt.currentTarget;
if (item.fliping) {
pic1Clicked() {
this.playAudio(this.data.audio_url);
}
pic2Clicked() {
this.playAudio(this.data.audio_url_2);
}
mapDown(event) {
if (!this.canTouch) {
return;
}
if ( this.checkClickTarget(this.btnLeft) ) {
this.btnLeftClicked();
return; return;
} }
if (this.pairNode.length === 2) {
if ( this.checkClickTarget(this.btnRight) ) {
this.btnRightClicked();
return;
}
if ( this.checkClickTarget(this.pic1) ) {
this.pic1Clicked();
return; return;
} }
item.fliping = true;
if ( this.checkClickTarget(this.pic2) ) { item.type = type;
this.pic2Clicked(); item.word = word;
return; if (this.pairNode.length === 0) {
this.pairNode[0] = item;
// do open card
this.doFlipCard(item, 'open');
} else if (this.pairNode.length === 1) {
if (this.pairNode[0] === item) {
// close selected card
this.doFlipCard(item, 'close');
} else {
this.pairNode[1] = item;
// open another card
this.doFlipCard(item, 'open');
// this.ANIMATING = true;
}
} }
}
mapMove(event) {
}
mapUp(event) {
}
update() {
// ----------------------------------------------------------
this.animationId = window.requestAnimationFrame(this.update.bind(this));
// 清除画布内容
this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
// tween 更新动画
TWEEN.update();
// ----------------------------------------------------------
this.updateArr(this.renderArr);
} }
......
@mixin hide-overflow-text {
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
@mixin k-no-select {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
@mixin k-img-bg {
background-repeat: no-repeat;
background-position: center;
background-size: contain;
}
/* You can add global styles to this file, and also import other style files */
/* You can add global styles to this file, and also import other style files */
/* You can add global styles to this file, and also import other style files */
html, body {
width: 100%;
height: 100%;
font-size: 16px;
}
body {
background-color: #f0f2f5 !important;
}
* {
outline: none;
}
@font-face {
font-family: ChalkboardSE-Regular;
src: url('/assets/font/ChalkboardSE-Regular.woff') format('WOFF');
}
.k-16-9 {
position: relative;
width: 100%;
height: fit-content;
&:before {
content: "";
display: block;
position: relative;
width: 100%;
height: 0;
padding-bottom: 56.25%;
}
}
.k-full-fill {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
}
.k-flex-center {
display: flex;
align-items: center;
justify-content: center;
}
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