Commit c32632dd authored by 李维's avatar 李维

ready for dev

parent a970169f
# ng-template-generator # ng-template-generator
angularjs技术框架下的H5互动模板框架脚手架,基于NG-ZORRO实现快速开发基于绘玩云的H5互动课件。 angularjs技术框架下的H5互动模板框架脚手架
\ No newline at end of file
# 使用简介
## 生成项目
* 登录绘玩云课件开发平台:http://staging-teach.ireadabc.com/
* 点击“登录账号,查看我的课件”
* 输入测试的用户名/密码:developers/12345678
* 在右上角“个人中心”的下拉菜单里,点击“我的模板” 菜单,然后点击“新建模板”, 填写必要的信息,在“技术选型”一项上选择“Angular”
* 点击“确定”后,列表页就会出现一个新生成的模板项目
* 在项目的卡片下找到“开发”按钮,则会弹出相对应的git地址
## 获取并启动项目
```
// xxx 是上面项目对应的Git地址
git clone xxx
cd 项目名称/
npm install
npm start
//启动成功后打开浏览器,输入:http://localhost:4200 则可以看到这个项目的初始化效果了
```
## 项目结构
|-- bin
|-- dist
|-- e2e
|-- node_modules
|-- publish
|-- src
| |-- app
| | |-- common
| | |-- form
| | |-- pipes
| | |-- play
| | |-- services
| | |-- style
| | |-- app.component.html
| | |-- app.component.scss
| | |-- app.component.ts
| | |-- app.module.ts
| |-- assets
| |-- environments
| |-- services
| |-- index.html
| |-- main.ts
| |--.......
* 其中 play 文件夹是H5模板展示页面,form文件夹是模板的配置页面
* common文件夹是以往做过的模板积累的一些比较常用的 AngularJs 的组件
* assets文件夹存放模板样式的静态资源,例如小图标、背景图片、字体、样式等等
* 其他文件夹开发者可以不做任何更改
## 开始开发
本脚手架的页面类UI框架是基于 [NG-ZORRO](https://ng.ant.design/docs/introduce/zh "With a Title") 框架实现的,在配置页面(form文件夹),一些表单输入,上传图片等组件也可以使用 NG-ZORRO 现成的组件
### 开发配置页面(form/)
配置页面主要作用是为了呈现多样化模板,提高H5模板的高复用性、灵活性提供的一个配置工具,根据H5模板的设计规划或特性,会规定某些元素是可以配置的,比如背景图片、音频、选项、标题等等等等。(课件制作者) 通过配置页面,就可以灵活的更换这些,从而形成多种多样的课件。
配置页面的开发要求很简单,我们不关心布局、样式、交互等所有实现细节与效果,只需调用两个内置的全局接口即可:
* setData 接口,用于将配置的数据存储到云端。
此方法是一个异步的方法,接收三个必要参数
obj: 一个json对象
callback: 回调函数
t_name: 模板名称
一般使用例子如下:
```
// ts语法,不能直接使用js的window对象自定义方法
(<any> window).courseware.setData(obj, callback, t_name);
```
* getData 接口,用于当点击修改的时候将配置的数据从云端获取下来。
此方法是一个异步的方法,接收两个必要参数
> callback: 回调函数, 回调函数里会得到云端保存的数据
> t_name: 模板名称
一般使用例子如下:
```
// ts语法,不能直接使用js的window对象自定义方法
const getData = (<any> window).courseware.getData;
getData((data) => {
//data 就是云端获取的数据,也就是setData方法存到云端的那个obj
//模板开发者需要根据数据,依次填写到表单的相应位置,方便用户二次修改编辑
});
```
### 开发展示页面(play/)
展示页面主要是呈现模板,通过H5技术,实现多样化的交互与展示方式。
展示页面更简单,开发者可以运用各种技术,实现Web端可以实现的任何效果,包括2D、3D、动画、游戏等各种场景,把表单配置的数据获取到,用于渲染相对应的页面即可:
* getData 接口,用于当点击修改的时候将配置的数据从云端获取下来。
此方法是一个异步的方法,接收两个必要参数
> callback: 回调函数, 回调函数里会得到云端保存的数据
> t_name: 模板名称
一般使用例子如下:
```
// ts语法,不能直接使用js的window对象自定义方法
const getData = (<any> window).courseware.getData;
getData((data) => {
//data 就是云端获取的数据,也就是setData方法存到云端的那个obj
//模板开发者需要根据数据来渲染或控制展示页面的呈现,达到模板的互动目的
});
```
### 互动课件
通过H5的交互方式,我们可以很轻松的制作在线的互动课件,使老师端与学生端实现联动,使在线课堂更具有交互性。
* onEvent 定义事件:用于自定义同步事件,配合多端互动使用,与sendEvent方法配合使用,用于监听多端发送的同步事件
参数
> evtName : 自定义事件的名称
> callback : 回调函数,回调函数里会得到传过来的数据
使用例子:
```
const cw = (<any> window).courseware;
//订阅一个事件
cw.onEvent('testEvent', (data,next) => {
console.log(data);
//处理事件的同步逻辑
//在逻辑处理完之后一定要执行next方法,用于解锁事件队列
next();
});
```
* sendEvent 发送事件:用于自定义同步事件,配合多端互动使用,与onEvent方法配合使用,用于对端发送同步事件
参数
> evtName : 自定义事件的名称
> data : 传递的参数
使用例子:
```
const cw = (<any> window).courseware;
//发送事件
//这样,所有端(教师、学生) 都会触发 testEvent 方法
cw.sendEvent('testEvent', 'Hello world');
```
* storeAspect 存储切面数据: 课件的切面状态数据;互动课件需要每一个开发者设计切面状态,用于学生进入已经进行的课件,恢复课件同步。例如老师讲图片轮播到第四页,这个时候学生进入教师,要恢复到第四页,才能跟老师同步上课。
参数
> data : 保存的数据,一个JSON对象
使用例子:
```
const cw = (<any> window).courseware;
//存储切面数据,用于学生进入教室,同步课堂信息等
cw.storeAspect({page: 5});
```
* getAspect 获取切面数据: 课件的切面状态数据;互动课件需要每一个开发者设计切面状态,用于学生进入已经进行的课件,恢复课件同步。例如老师讲图片轮播到第四页,这个时候学生进入教师,要恢复到第四页,才能跟老师同步上课。
参数
> callback : 获取切面数据的回调函数
使用例子:
```
const cw = (<any> window).courseware;
//存储切面数据,用于学生进入教室,同步课堂信息等
cw.getAspect(function(aspect){
//恢复切面状态的逻辑
});
```
* 补充项
1) 有时候我们在实现模板效果的时候,需要一些基本的信息,比如教室信息、当前用户信息等等,例如,老师会显示某个按钮,而学生不显示,我们可以用如下方式获取教室信息;后续大部分静态信息都会完善到这个对象里
```
//获取教室信息,值得注意的是获取这个信息一定要再 getData方法之后,或者确保页面完成之后
getData((data, aspect) => {
let airClassInfo = window["air"].airClassInfo;
console.log(airClassInfo);
});
```
2) 互动课件的 getData 方法的回调函数里多返回了一个切面参数,类似如下:
```
const getData = (<any> window).courseware.getData;
getData((data, aspect) => {
//data 就是云端获取的数据,也就是setData方法存到云端的那个obj
//aspect 切面参数,用于恢复页面的状态
//模板开发者需要根据数据来渲染或控制展示页面的呈现,达到模板的互动目的
});
```
3) 测试互动课件
我们在本地开发中,无法模拟线上老师和学生互动的交互场景,所以提供了一套开发者测试的账号,如下:
测试地址:http://staging-ac.ireadabc.com/
老师用户名/密码:devtea / 1
学生用户名/密码: 学生1(13877777711 / 1) 学生2(13877777712 / 1)
测试方法:
1、首先开发者发布完模板之后,在制作课件的菜单下用这个模板制作一个课件
2、在本地打开两个浏览器窗口 一个登陆老师用户,另一个登陆学生用户
3、老师用户进去之后,就会有很多课件,这个时候双击自己制作的课件,学生的窗口就会同步的打开课件了
4、测试自己的交互事件,看看老师端和学生端是否是自己想要的展示效果
### 补充方法
在模板的编辑或展示过程中,还会经常遇到如下两个场景,特提供针对性的解决办法
1) 表单录入往往需要上传图片、音视频等多媒体文件,所以脚手架内置如下方法,用于获取一个上传到云端的接口方法
```
const uploadUrl = (<any> window).courseware.uploadUrl;
const uploadData = (<any> window).courseware.uploadData;
//例如用于NG-ZORRO的上传组件则如下写法:
<nz-upload class="p-image-uploader" [nzDisabled]="disableUpload"
[nzShowUploadList]="false"
nzAccept = "image/*"
[nzAction]="uploadUrl" <!-- 注意这里 -->
[nzData]="uploadData" <!-- 注意这里 -->
(nzChange)="handleChange($event)">
</nz-upload>
```
2) 在展示页面加载完成的时候,需要调用一下下面的方法,用于释放切换课件的遮罩层
```
//此方法为固定写法,
//其中t_name为模板名称,obj是云端存储的配置数据
window["air"].hideAirClassLoading(t_name,obj);
```
## 打包发布
模板开发完成之后,要推送到云平台上使用则需要进行打包发布操作
```
npm run publish
```
在项目根目录下执行上述命令,则在 ./publish 目录下生产一个 .zip的压缩包,此时打开云平台
http://staging-teach.ireadabc.com/
点击“模板管理” 菜单,找到对应的模板卡片,点击“发布”按钮,在弹出的对话框中选中压缩包,然后点击“确定”,上传完成后,则发布就成功了
\ No newline at end of file
...@@ -3,9 +3,13 @@ ...@@ -3,9 +3,13 @@
"version": 1, "version": 1,
"newProjectRoot": "projects", "newProjectRoot": "projects",
"projects": { "projects": {
"ng-template-generator": { "ng-one": {
"projectType": "application", "projectType": "application",
"schematics": {}, "schematics": {
"@schematics/angular:component": {
"style": "scss"
}
},
"root": "", "root": "",
"sourceRoot": "src", "sourceRoot": "src",
"prefix": "app", "prefix": "app",
...@@ -13,26 +17,39 @@ ...@@ -13,26 +17,39 @@
"build": { "build": {
"builder": "@angular-devkit/build-angular:browser", "builder": "@angular-devkit/build-angular:browser",
"options": { "options": {
"outputPath": "dist/ng-template-generator", "outputPath": "dist",
"index": "src/index.html", "index": "src/index.html",
"main": "src/main.ts", "main": "src/main.ts",
"polyfills": "src/polyfills.ts", "polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json", "tsConfig": "tsconfig.app.json",
"aot": true, "aot": false,
"assets": [ "assets": [
"src/favicon.ico", "src/favicon.ico",
"src/assets", "src/assets",
{ "glob": "**/*", "input": "src/assets/libs/service-worker/", "output": "/" },
{ {
"glob": "**/*", "glob": "**/*",
"input": "./node_modules/@ant-design/icons-angular/src/inline-svg/", "input": "./node_modules/@ant-design/icons-angular/src/inline-svg/",
"output": "/assets/" "output": "/assets/"
},
{
"glob": "**/*",
"input": "./dist/game/",
"output": "/assets/cocos/"
} }
], ],
"styles": [ "styles": [
"src/styles.scss",
"./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"
], ],
"scripts": [] "scripts": [
"src/assets/libs/audio-recorder/lame.min.js",
"src/assets/libs/audio-recorder/worker.js",
"src/assets/libs/audio-recorder/recorder.js"
]
}, },
"configurations": { "configurations": {
"production": { "production": {
...@@ -47,39 +64,28 @@ ...@@ -47,39 +64,28 @@
"sourceMap": false, "sourceMap": false,
"extractCss": true, "extractCss": true,
"namedChunks": false, "namedChunks": false,
"aot": true,
"extractLicenses": true, "extractLicenses": true,
"vendorChunk": false, "vendorChunk": false,
"buildOptimizer": true, "buildOptimizer": true
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "6kb",
"maximumError": "10kb"
}
]
} }
} }
}, },
"serve": { "serve": {
"builder": "@angular-devkit/build-angular:dev-server", "builder": "@angular-devkit/build-angular:dev-server",
"options": { "options": {
"browserTarget": "ng-template-generator:build" "browserTarget": "ng-one:build"
}, },
"configurations": { "configurations": {
"production": { "production": {
"browserTarget": "ng-template-generator:build:production" "browserTarget": "ng-one:build:production"
} }
} }
}, },
"extract-i18n": { "extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n", "builder": "@angular-devkit/build-angular:extract-i18n",
"options": { "options": {
"browserTarget": "ng-template-generator:build" "browserTarget": "ng-one:build"
} }
}, },
"test": { "test": {
...@@ -94,8 +100,7 @@ ...@@ -94,8 +100,7 @@
"src/assets" "src/assets"
], ],
"styles": [ "styles": [
"./node_modules/ng-zorro-antd/ng-zorro-antd.min.css", "src/styles.scss"
"src/styles.css"
], ],
"scripts": [] "scripts": []
} }
...@@ -117,16 +122,15 @@ ...@@ -117,16 +122,15 @@
"builder": "@angular-devkit/build-angular:protractor", "builder": "@angular-devkit/build-angular:protractor",
"options": { "options": {
"protractorConfig": "e2e/protractor.conf.js", "protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "ng-template-generator:serve" "devServerTarget": "ng-one:serve"
}, },
"configurations": { "configurations": {
"production": { "production": {
"devServerTarget": "ng-template-generator:serve:production" "devServerTarget": "ng-one:serve:production"
} }
} }
} }
} }
} }},
}, "defaultProject": "ng-one"
"defaultProject": "ng-template-generator"
} }
\ No newline at end of file
...@@ -66,7 +66,7 @@ const runSpawn = async function (){ ...@@ -66,7 +66,7 @@ const runSpawn = async function (){
ls.on('close', (code) => { ls.on('close', (code) => {
console.log(`child process exited with code ${code}`); console.log(`child process exited with code ${code}`);
//要压缩的目录 //要压缩的目录
let zippath = path.resolve(__dirname,"../dist", pkg.name); let zippath = path.resolve(__dirname,"../dist");
//压缩包的存放目录 //压缩包的存放目录
let date = new Date(); let date = new Date();
let zipname = pkg.name+"_"+date.Format("yyyyMMdd hh-mm-ss"); let zipname = pkg.name+"_"+date.Format("yyyyMMdd hh-mm-ss");
...@@ -112,3 +112,4 @@ exec(); ...@@ -112,3 +112,4 @@ exec();
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -2,56 +2,74 @@ ...@@ -2,56 +2,74 @@
"name": "ng-template-generator", "name": "ng-template-generator",
"version": "0.0.1", "version": "0.0.1",
"scripts": { "scripts": {
"start": "ng serve", "start": "ng serve --host=0.0.0.0",
"build": "ng build --build--optimizer --aot --base-href /JGT/v3/", "build": "ng build --build--optimizer --aot --base-href /JGT/v3/",
"publish": "node ./bin/publish.js", "publish": "node ./bin/publish.js"
"ng": "ng",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
}, },
"private": true, "private": true,
"dependencies": { "dependencies": {
"@angular/animations": "~9.0.2", "@angular/animations": "^7.2.10",
"@angular/common": "~9.0.2", "@angular/cdk": "^7.2.2",
"@angular/compiler": "~9.0.2", "@angular/common": "^7.2.10",
"@angular/core": "~9.0.2", "@angular/compiler": "^7.2.10",
"@angular/forms": "~9.0.2", "@angular/core": "^7.2.10",
"@angular/platform-browser": "~9.0.2", "@angular/flex-layout": "^7.0.0-beta.24",
"@angular/platform-browser-dynamic": "~9.0.2", "@angular/forms": "^7.2.10",
"@angular/router": "~9.0.2", "@angular/http": "^7.2.10",
"@fortawesome/angular-fontawesome": "^0.6.0", "@angular/material": "^7.2.2",
"@fortawesome/fontawesome-svg-core": "^1.2.27", "@angular/platform-browser": "^7.2.10",
"@fortawesome/free-regular-svg-icons": "^5.12.1", "@angular/platform-browser-dynamic": "^7.2.10",
"@fortawesome/free-solid-svg-icons": "^5.12.1", "@angular/platform-server": "^7.2.10",
"@tweenjs/tween.js": "^18.5.0", "@angular/router": "^7.2.10",
"ali-oss": "^6.5.1", "@tweenjs/tween.js": "^17.3.0",
"compressing": "^1.5.0", "ali-oss": "^6.0.0",
"ng-zorro-antd": "^8.5.2", "angular-cropperjs": "^1.0.1",
"rxjs": "~6.5.4", "angular2-draggable": "^2.1.9",
"angular2-fontawesome": "^5.2.1",
"angularx-qrcode": "^1.5.3",
"animate.css": "^3.7.0",
"bootstrap": "^4.1.1",
"browser-image-compression": "^1.0.5",
"compressing": "^1.4.0",
"core-js": "^2.6.1",
"cropperjs": "1.4.1",
"css-element-queries": "^1.0.2",
"decimal.js": "^10.0.1",
"font-awesome": "^4.7.0",
"hammerjs": "^2.0.8",
"install": "^0.12.2",
"karma-cli": "^2.0.0",
"lodash": "^4.17.10",
"nedb": "^1.8.0",
"ng-lottie": "^0.3.1",
"ng-zorro-antd": "^7.2.0",
"npm": "^6.5.0",
"rxjs": "^6.3.3",
"rxjs-compat": "^6.3.3",
"rxjs-tslint": "^0.1.6",
"spark-md5": "^3.0.0", "spark-md5": "^3.0.0",
"tslib": "^1.10.0", "webpack": "^4.28.2",
"zone.js": "~0.10.2" "zone.js": "^0.8.26"
}, },
"devDependencies": { "devDependencies": {
"@angular-devkit/build-angular": "~0.900.3", "@angular-devkit/build-angular": "^0.11.4",
"@angular/cli": "~9.0.3", "@angular/cli": "^7.2.10",
"@angular/compiler-cli": "~9.0.2", "@angular/compiler-cli": "^7.2.10",
"@angular/language-service": "~9.0.2", "@angular/language-service": "^7.2.10",
"@types/jasmine": "~3.5.0", "@types/jasmine": "^3.3.5",
"@types/jasminewd2": "~2.0.3", "@types/jasminewd2": "~2.0.3",
"@types/node": "^12.11.1", "@types/node": "^10.12.18",
"codelyzer": "^5.1.2", "codelyzer": "^4.5.0",
"jasmine-core": "~3.5.0", "jasmine-core": "^3.3.0",
"jasmine-spec-reporter": "~4.2.1", "jasmine-spec-reporter": "^4.2.1",
"karma": "~4.3.0", "karma": "^3.1.4",
"karma-chrome-launcher": "~3.1.0", "karma-chrome-launcher": "^2.2.0",
"karma-coverage-istanbul-reporter": "~2.1.0", "karma-coverage-istanbul-reporter": "~2.0.0",
"karma-jasmine": "~2.0.1", "karma-jasmine": "^2.0.1",
"karma-jasmine-html-reporter": "^1.4.2", "karma-jasmine-html-reporter": "^1.4.0",
"protractor": "~5.4.3", "protractor": "^5.4.2",
"ts-node": "~8.3.0", "ts-node": "~5.0.1",
"tslint": "~5.18.0", "tslint": "^5.12.0",
"typescript": "~3.7.5" "typescript": "3.1.1"
} }
} }
...@@ -5,12 +5,12 @@ import { Component , OnInit} from '@angular/core'; ...@@ -5,12 +5,12 @@ import { Component , OnInit} from '@angular/core';
templateUrl: './app.component.html', templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'] styleUrls: ['./app.component.scss']
}) })
export class AppComponent implements OnInit { export class AppComponent implements OnInit{
type = 'play'; type = 'play';
constructor() { constructor() {
const tp = this.getQueryString('type'); let tp = this.getQueryString("type");
if (tp) { if (tp){
this.type = tp; this.type = tp;
} }
} }
......
import { BrowserModule } from '@angular/platform-browser'; import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { NgZorroAntdModule, NZ_I18N, zh_CN } from 'ng-zorro-antd';
import { FormsModule } from '@angular/forms'; import { FormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http'; import { HttpClientModule } from '@angular/common/http';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { NgZorroAntdModule, NZ_I18N, zh_CN } from 'ng-zorro-antd';
import { NzButtonModule } from 'ng-zorro-antd/button';
import {Angular2FontawesomeModule} from 'angular2-fontawesome/angular2-fontawesome';
import { AppComponent } from './app.component';
import { FormComponent } from './form/form.component';
import { PlayComponent } from "./play/play.component";
import { LessonTitleConfigComponent } from './common/lesson-title-config/lesson-title-config.component';
import { AudioRecorderComponent } from './common/audio-recorder/audio-recorder.component';
import { PlayerContentWrapperComponent } from './common/player-content-wrapper/player-content-wrapper.component';
/** 配置 angular i18n **/
import { registerLocaleData } from '@angular/common'; import { registerLocaleData } from '@angular/common';
import zh from '@angular/common/locales/zh'; import zh from '@angular/common/locales/zh';
import {FormComponent} from './form/form.component'; import {UploadImageWithPreviewComponent} from "./common/upload-image-with-preview/upload-image-with-preview.component";
import {PlayComponent} from './play/play.component'; import {BackgroundImagePipe} from "./pipes/background-image.pipe";
import {LessonTitleConfigComponent} from './common/lesson-title-config/lesson-title-config.component'; import {UploadVideoComponent} from "./common/upload-video/upload-video.component";
import {BackgroundImagePipe} from './pipes/background-image.pipe'; import {ResourcePipe} from "./pipes/resource.pipe";
import {UploadImageWithPreviewComponent} from './common/upload-image-with-preview/upload-image-with-preview.component'; import {TimePipe} from "./pipes/time.pipe";
import {PlayerContentWrapperComponent} from './common/player-content-wrapper/player-content-wrapper.component'; import {CustomHotZoneComponent} from "./common/custom-hot-zone/custom-hot-zone.component";
import {CustomHotZoneComponent} from './common/custom-hot-zone/custom-hot-zone.component';
import {UploadVideoComponent} from './common/upload-video/upload-video.component';
import {TimePipe} from './pipes/time.pipe';
import {ResourcePipe} from './pipes/resource.pipe';
import {AudioRecorderComponent} from './common/audio-recorder/audio-recorder.component';
import { FontAwesomeModule, FaIconLibrary } from '@fortawesome/angular-fontawesome';
import { fas } from '@fortawesome/free-solid-svg-icons';
import { far } from '@fortawesome/free-regular-svg-icons';
registerLocaleData(zh); registerLocaleData(zh);
@NgModule({ @NgModule({
...@@ -38,23 +41,21 @@ registerLocaleData(zh); ...@@ -38,23 +41,21 @@ registerLocaleData(zh);
TimePipe, TimePipe,
UploadVideoComponent, UploadVideoComponent,
CustomHotZoneComponent, CustomHotZoneComponent,
PlayerContentWrapperComponent PlayerContentWrapperComponent
], ],
imports: [ imports: [
BrowserModule,
NgZorroAntdModule,
FormsModule, FormsModule,
HttpClientModule, HttpClientModule,
BrowserAnimationsModule, BrowserAnimationsModule,
FontAwesomeModule BrowserModule,
Angular2FontawesomeModule,
NgZorroAntdModule,
],
/** 配置 ng-zorro-antd 国际化(文案 及 日期) **/
providers : [
{ provide: NZ_I18N, useValue: zh_CN }
], ],
providers: [{ provide: NZ_I18N, useValue: zh_CN }],
bootstrap: [AppComponent] bootstrap: [AppComponent]
}) })
export class AppModule { export class AppModule { }
constructor(library: FaIconLibrary) {
library.addIconPacks(fas, far);
}
}
<div class="d-flex"> <div class="d-flex">
<div class="p-btn-record d-flex"> <div class="p-btn-record d-flex">
<div class="btn-clear" style="cursor: pointer" (click)="onBtnClearAudio()" *ngIf="withRmBtn && (audioUrl || audioBlob)"> <div
<fa-icon icon="times"></fa-icon> class="btn-clear"
(click)="onBtnClearAudio()"
*ngIf="withRmBtn && (audioUrl || audioBlob)"
>
<fa name="close"></fa>
</div> </div>
<div class="btn-record" *ngIf="type===Type.RECORD && !isUploading" <div
class="btn-record"
*ngIf="type === Type.RECORD && !isUploading"
[class.p-recording]="isRecording" [class.p-recording]="isRecording"
(click)="onBtnRecord()"> (click)="onBtnRecord()"
<fa-icon icon="microphone"></fa-icon> >
<i nz-icon nzType="audio" nzTheme="outline"></i>
Record Audio Record Audio
</div> </div>
<nz-upload <nz-upload
[nzAccept] = "'.mp3'" [nzAccept]="'.mp3'"
[nzShowUploadList]="false" [nzShowUploadList]="false"
[nzAction]="uploadUrl" [nzAction]="uploadUrl"
[nzData]="uploadData" [nzData]="uploadData"
(nzChange)="handleChange($event)"> (nzChange)="handleChange($event)"
>
<div class="btn-upload" [ngClass]="{'has-clear': withRmBtn && (audioUrl || audioBlob)}" *ngIf="type===Type.UPLOAD && !isUploading"> <div
<fa-icon icon="upload"></fa-icon> class="btn-upload ng-star-inserted"
[ngClass]="{ 'has-clear': withRmBtn && (audioUrl || audioBlob) }"
*ngIf="type === Type.UPLOAD && !isUploading"
>
<i nz-icon nzType="cloud-upload" nzTheme="outline"></i>
Upload Audio Upload Audio
</div> </div>
</nz-upload> </nz-upload>
<div class="p-upload-progress-bg" *ngIf="isUploading"> <div class="p-upload-progress-bg" *ngIf="isUploading">
<div class="i-bg" [style.width]="progress+'%'"></div> <div class="i-bg" [style.width]="progress + '%'"></div>
<div class="i-text"> <div class="i-text">
<fa-icon icon="cloud-upload-alt"></fa-icon> <i nz-icon nzType="loading" nzTheme="outline"></i>
Uploading... Uploading...
</div> </div>
</div> </div>
<div
*ngIf="audioUrl && needRemove; then truthyTemplate; else falsyTemplate"
></div>
<div *ngIf="audioUrl && needRemove; then truthyTemplate else falsyTemplate"></div> <ng-template #truthyTemplate>
<ng-template #truthyTemplate >
<div class="btn-delete" (click)="onBtnDeleteAudio()"> <div class="btn-delete" (click)="onBtnDeleteAudio()">
<fa-icon icon="close"></fa-icon> <i nz-icon nzType="close" nzTheme="outline"></i>
</div> </div>
</ng-template> </ng-template>
<ng-template #falsyTemplate> <ng-template #falsyTemplate>
<div class="btn-switch" (click)="onBtnSwitchType()"> <div class="btn-switch" (click)="onBtnSwitchType()">
<fa-icon icon="cog"></fa-icon> <i nz-icon nzType="setting" nzTheme="outline"></i>
</div> </div>
</ng-template> </ng-template>
</div> </div>
<div class="p-progress ml-2" (click)="onBtnPlay()" *ngIf="audioUrl || audioBlob"> <div
<nz-progress [nzPercent]="percent" [nzWidth]="30" [nzFormat]="progressText" class="p-progress ml-2"
nzType="circle"></nz-progress> (click)="onBtnPlay()"
<div class="p-btn-play" [style.left]="isPlaying?'8px':''"> *ngIf="audioUrl || audioBlob"
<fa-icon [icon]="playIcon"></fa-icon> >
<nz-progress
[nzPercent]="percent"
[nzWidth]="30"
[nzFormat]="progressText"
nzType="circle"
></nz-progress>
<div class="p-btn-play" [style.left]="isPlaying ? '8px' : ''">
<i nz-icon nzType="caret-right" nzTheme="outline"></i>
</div> </div>
</div> </div>
</div> </div>
.d-flex{
display: flex;
}
.p-btn-record { .p-btn-record {
font-size: 0.9rem; font-size: 0.9rem;
color: #555; color: #555;
...@@ -91,6 +88,7 @@ ...@@ -91,6 +88,7 @@
.p-progress { .p-progress {
margin-top: 2px; margin-top: 2px;
margin-left: 5px;
position: relative; position: relative;
line-height: 26px; line-height: 26px;
.p-btn-play { .p-btn-play {
...@@ -105,3 +103,6 @@ ...@@ -105,3 +103,6 @@
line-height: 33px; line-height: 33px;
} }
.d-flex{
display: flex;
}
\ No newline at end of file
...@@ -19,11 +19,10 @@ export class AudioRecorderComponent implements OnInit, OnChanges, OnDestroy { ...@@ -19,11 +19,10 @@ export class AudioRecorderComponent implements OnInit, OnChanges, OnDestroy {
isUploading = false; isUploading = false;
type = Type.UPLOAD; // record | upload type = Type.UPLOAD; // record | upload
Type = Type; Type = Type;
@Input()
withRmBtn = false; withRmBtn = false;
uploadUrl = (window as any).courseware.uploadUrl(); uploadUrl = (<any>window).courseware.uploadUrl();
uploadData = (window as any).courseware.uploadData(); uploadData = (<any>window).courseware.uploadData();
@Input() @Input()
needRemove = false; needRemove = false;
...@@ -33,7 +32,7 @@ export class AudioRecorderComponent implements OnInit, OnChanges, OnDestroy { ...@@ -33,7 +32,7 @@ export class AudioRecorderComponent implements OnInit, OnChanges, OnDestroy {
@Input() @Input()
set audioUrl(url) { set audioUrl(url) {
this._audioUrl = url; this._audioUrl = url
if (url) { if (url) {
this.audio.src = this._audioUrl; this.audio.src = this._audioUrl;
this.audio.load(); this.audio.load();
...@@ -145,7 +144,6 @@ export class AudioRecorderComponent implements OnInit, OnChanges, OnDestroy { ...@@ -145,7 +144,6 @@ export class AudioRecorderComponent implements OnInit, OnChanges, OnDestroy {
onBtnSwitchType() { onBtnSwitchType() {
} }
onBtnClearAudio() { onBtnClearAudio() {
this.audioUrl = null;
this.audioRemoved.emit(); this.audioRemoved.emit();
} }
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
<div class="bg-box"> <div class="bg-box">
<app-upload-image-with-preview <app-upload-image-with-preview
[picUrl]="bgItem?.url" [picUrl]="bgItem.url"
(imageUploaded)="onBackgroundUploadSuccess($event)"> (imageUploaded)="onBackgroundUploadSuccess($event)">
</app-upload-image-with-preview> </app-upload-image-with-preview>
</div> </div>
...@@ -76,7 +76,7 @@ ...@@ -76,7 +76,7 @@
<button class="save-btn" nz-button nzType="primary" [nzSize]="'large'" nzShape="round" <button class="save-btn" nz-button nzType="primary" [nzSize]="'large'" nzShape="round"
(click)="saveClick()"> (click)="saveClick()">
<i nz-icon nzType="save"></i> <i nz-icon type="save"></i>
Save Save
</button> </button>
......
...@@ -42,8 +42,8 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges { ...@@ -42,8 +42,8 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
@Output() @Output()
save = new EventEmitter(); save = new EventEmitter();
@ViewChild('canvas', {static: true }) canvas: ElementRef; @ViewChild('canvas') canvas: ElementRef;
@ViewChild('wrap', {static: true }) wrap: ElementRef; @ViewChild('wrap') wrap: ElementRef;
// @HostListener('window:resize', ['$event']) // @HostListener('window:resize', ['$event'])
canvasWidth = 1280; canvasWidth = 1280;
...@@ -613,9 +613,7 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges { ...@@ -613,9 +613,7 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
update() { update() {
if (!this.ctx) {
return;
}
this.animationId = window.requestAnimationFrame(this.update.bind(this)); this.animationId = window.requestAnimationFrame(this.update.bind(this));
......
<div class="title-config"> <div class="title-config">
<div class="title-wrap"> <div class="title-wrap">
...@@ -8,10 +7,9 @@ ...@@ -8,10 +7,9 @@
<nz-select class="ml-1" style="width: 120px;" [(ngModel)]="__fontFamily" <nz-select class="ml-1" style="width: 120px;" [(ngModel)]="__fontFamily"
(ngModelChange)="onChangeFontFamily($event)" (ngModelChange)="onChangeFontFamily($event)"
nzPlaceHolder="Font Family" nzPlaceHolder="Font Family"
[nzDropdownMatchSelectWidth]="false"> [nzDropdownMatchSelectWidth]="false"
<nz-option [nzValue]="font" nzCustomContent [nzLabel]="font" *ngFor="let font of fontFamilyList"> >
<span [ngStyle]="{'font-family': font}" >{{font}}</span> <nz-option [nzValue]="font" [nzLabel]="font" *ngFor="let font of fontFamilyList"></nz-option>
</nz-option>
</nz-select> </nz-select>
<nz-select class="ml-1" style="width: 110px;" [(ngModel)]="__fontSize" <nz-select class="ml-1" style="width: 110px;" [(ngModel)]="__fontSize"
(ngModelChange)="onChangeFontSize()" (ngModelChange)="onChangeFontSize()"
...@@ -21,33 +19,28 @@ ...@@ -21,33 +19,28 @@
<div class="p-divider"></div> <div class="p-divider"></div>
<div class="i-tool-font-btn d-flex mr-2"> <div class="i-tool-font-btn d-flex mr-2">
<div class="position-relative fa-icon" (click)="onChangeBold()"> <div class="position-relative fa-icon" (click)="onChangeBold()">
<!-- <div class="fa fa-bold"></div>--> <div class="fa fa-bold"></div>
<fa-icon icon="bold"></fa-icon>
</div> </div>
</div> </div>
<div class="i-tool-font-btn d-flex mr-2"> <div class="i-tool-font-btn d-flex mr-2">
<div class="position-relative fa-icon" (click)="onChangeItalic()"> <div class="position-relative fa-icon" (click)="onChangeItalic()">
<!-- <div class="fa fa-italic"></div>--> <div class="fa fa-italic"></div>
<fa-icon icon="italic"></fa-icon>
</div> </div>
</div> </div>
<div class="i-tool-font-btn d-flex mr-2"> <div class="i-tool-font-btn d-flex mr-2">
<div class="position-relative fa-icon" (click)="onChangeUnderline()"> <div class="position-relative fa-icon" (click)="onChangeUnderline()">
<!-- <div class="fa fa-underline"></div>--> <div class="fa fa-underline"></div>
<fa-icon icon="underline"></fa-icon>
</div> </div>
</div> </div>
<div class="i-tool-font-btn d-flex"> <div class="i-tool-font-btn d-flex">
<div class="position-relative fa-icon" (click)="onChangeStrikethrough()"> <div class="position-relative fa-icon" (click)="onChangeStrikethrough()">
<!-- <div class="fa fa-strikethrough"></div>--> <div class="fa fa-strikethrough"></div>
<fa-icon icon="strikethrough"></fa-icon>
</div> </div>
</div> </div>
<div class="p-divider"></div> <div class="p-divider"></div>
<div class="i-tool-font-color d-flex"> <div class="i-tool-font-color d-flex">
<div class="position-relative i-left flex-fill" (click)="onChangeFontColor($event)"> <div class="position-relative i-left flex-fill" (click)="onChangeFontColor($event)">
<!-- <div class="fa fa-font"></div>--> <div class="fa fa-font"></div>
<fa-icon icon="palette"></fa-icon>
<div class="i-color" [style.background-color]="__fontColor"></div> <div class="i-color" [style.background-color]="__fontColor"></div>
</div> </div>
<div class="i-dropdown-menu" nzPlacement="bottom" <div class="i-dropdown-menu" nzPlacement="bottom"
...@@ -58,14 +51,14 @@ ...@@ -58,14 +51,14 @@
</div> </div>
<div class="p-divider"></div> <div class="p-divider"></div>
<div style="background: #fff;display: block;"> <div style="background: #fff;display: block;">
<div class="position-relative"> <div class="position-relative" (click)="onChangeStrikethrough()">
<app-audio-recorder [audioUrl]="titleObj && titleObj.audio_url" (audioUploaded)="titleAudioUploaded($event)"></app-audio-recorder> <app-audio-recorder [audioUrl]="titleObj && titleObj.audio_url" (audioUploaded)="titleAudioUploaded($event)"></app-audio-recorder>
</div> </div>
</div> </div>
</div> </div>
<div class="width-100 d-flex"> <div class="width-100 d-flex">
<iframe #titleEl id="titleContentEgret" frameborder="0" style="overflow: hidden;width: 100%; height:48px; margin: 0; padding: 0;"></iframe> <iframe #titleEl frameborder="0" style="overflow: hidden;width: 100%; height:48px; margin: 0; padding: 0;"></iframe>
</div> </div>
</div> </div>
...@@ -83,11 +76,37 @@ ...@@ -83,11 +76,37 @@
<ng-container *ngIf="withIcon">
<div class="row type-row">
课程类型:
<nz-radio-group [(ngModel)]="titleObj && titleObj.type" (ngModelChange)="typeChange($event)">
<label nz-radio nzValue="a">单数课</label>
<label nz-radio nzValue="b">双数课</label>
<label nz-radio nzValue="c">复习课</label>
</nz-radio-group>
</div>
</ng-container>
</div> </div>
<ng-container *ngIf="withIcon">
<div class="title-icons">
<div class="icons-list">
<nz-checkbox-wrapper style="width: 100%;clear:both" (nzOnChange)="iconsChanges($event)">
<div [class]="'icon-item icon-'+i" *ngFor="let i of groupIconsCount[titleObj.type];">
<div class="img-box">
<nz-badge class="icon-badge" [nzCount]="titleObj && titleObj.icons && titleObj.icons.indexOf(i) + 1">
<img [src]="'assets/title-icons/'+titleObj.type+'/icon-'+i+'.png'" alt="">
</nz-badge>
</div>
<label nz-checkbox [nzValue]="i" [ngModel]="titleObj && titleObj.icons && titleObj.icons.indexOf(i) > -1"></label>
</div>
</nz-checkbox-wrapper>
</div>
</div>
</ng-container>
</div> </div>
@import '../../style/common_mixin.css'; @import '../../style/common_mixin';
.title-config { .title-config {
.letter-wrap{ .letter-wrap{
...@@ -11,73 +11,39 @@ ...@@ -11,73 +11,39 @@
.type-row{ .type-row{
margin: 0;padding-top: 1rem; margin: 0;padding-top: 1rem;
} }
.icon-item{
} margin-right: 16px;
@font-face float: left;
{ width: 45px;
font-family: 'BRLNSDB'; height: 75px;
src: url("../../../assets/font/BRLNSDB.TTF") ; display: flex;
} justify-content: center;
align-items: center;
@font-face position: relative;
{ .icon-badge{
font-family: 'BRLNSB'; position: absolute;
src: url("../../../assets/font/BRLNSB.TTF") ; top: 0;
} right: 0;
}
@font-face .img-box{
{ top: 0;
font-family: 'BRLNSR'; position: absolute;
src: url("../../../assets/font/BRLNSR.TTF") ; width: 45px;
} height: 50px;
display: flex;
@font-face justify-content: center;
{ align-items: center;
font-family: 'GOTHIC'; img{
src: url("../../../assets/font/GOTHIC.TTF") ; max-width: 100%;
} }
}
@font-face label{
{ position: absolute;
font-family: 'GOTHICB'; bottom: 0;
src: url("../../../assets/font/GOTHICB.TTF") ; }
} }
@font-face
{
font-family: 'GOTHICBI';
src: url("../../../assets/font/GOTHICBI.TTF") ;
}
@font-face
{
font-family: 'GOTHICI';
src: url("../../../assets/font/GOTHICI.TTF") ;
}
@font-face
{
font-family: 'MMTextBook';
src: url("../../../assets/font/MMTextBook.otf") ;
}
@font-face
{
font-family: 'MMTextBook-Bold';
src: url("../../../assets/font/MMTextBook-Bold.otf") ;
}
@font-face
{
font-family: 'MMTextBook-BoldItalic';
src: url("../../../assets/font/MMTextBook-BoldItalic.otf") ;
} }
@font-face
{
font-family: 'MMTextBook-Italic';
src: url("../../../assets/font/MMTextBook-Italic.otf") ;
}
@mixin tool-btn { @mixin tool-btn {
border: 1px solid #ddd; border: 1px solid #ddd;
display: flex; display: flex;
...@@ -88,37 +54,16 @@ ...@@ -88,37 +54,16 @@
border-radius: 6px; border-radius: 6px;
color: #555; color: #555;
} }
.d-flex{
display: flex;
}
.position-relative { .p-title-box {
position: relative; .p-title {
}
.flex-fill {
-webkit-box-flex: 1;
flex: 1 1 auto;
justify-content: center;
display: flex;
}
.i-dropdown-menu{
width: 15px;
font-size: 10px;
border-left: 1px solid #ddd;
display: -webkit-box;
display: flex;
-webkit-box-align: center;
align-items: center;
flex: 0
}
.p-title-box .p-title {
font-size: 20px; font-size: 20px;
} }
.p-title-box input { input {
width: 300px; width: 300px;
margin-left: 10px; margin-left: 10px;
} }
}
.p-content { .p-content {
border: 1px solid #ddd; border: 1px solid #ddd;
...@@ -140,31 +85,33 @@ ...@@ -140,31 +85,33 @@
align-items: center; align-items: center;
border-bottom: 1px solid #ddd; border-bottom: 1px solid #ddd;
// save
.i-tool-save {
} @include tool-btn();
// save
.i-tool-save {
//@include tool-btn();
color: white; color: white;
} }
.i-tool-save:disabled { .i-tool-save:disabled {
color: #555; color: #555;
} }
// font-size // font-size
.i-tool-font-size { .i-tool-font-size {
//@include tool-btn(); @include tool-btn();
width: 37px; width: 37px;
} & > span {
.i-tool-font-size:hover { position: absolute;
top: -5px;
right: 5px;
}
}
.i-tool-font-size:hover {
color: black; color: black;
border-color: #bbb; border-color: #bbb;
} }
// font-color // font-color
.i-tool-font-color, .i-tool-font-btn { .i-tool-font-color, .i-tool-font-btn {
border: 1px solid #ddd; border: 1px solid #ddd;
//padding: 3px 7px; //padding: 3px 7px;
border-radius: 6px; border-radius: 6px;
...@@ -208,20 +155,20 @@ ...@@ -208,20 +155,20 @@
transform: scale(0.6); transform: scale(0.6);
} }
} }
} }
.i-tool-font-btn{ .i-tool-font-btn{
width: 31px; width: 31px;
} }
.fa-icon{ .fa-icon{
width: 100%; width: 100%;
height: 100%; height: 100%;
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
cursor: pointer; cursor: pointer;
} }
// bg-color // bg-color
.i-tool-bg-color { .i-tool-bg-color {
@include tool-btn(); @include tool-btn();
padding: 0 9px; padding: 0 9px;
::ng-deep > span { ::ng-deep > span {
...@@ -236,14 +183,17 @@ ...@@ -236,14 +183,17 @@
background-color: white; background-color: white;
margin-left: 10px; margin-left: 10px;
} }
} }
// horizontal-center // horizontal-center
.i-tool-horizontal-center { .i-tool-horizontal-center {
@include tool-btn(); @include tool-btn();
width: 37px; width: 37px;
}
} }
.p-box { .p-box {
width: 1280px; width: 1280px;
height: 720px; height: 720px;
...@@ -253,7 +203,9 @@ ...@@ -253,7 +203,9 @@
overflow: hidden; overflow: hidden;
} }
.p-sentence {
@include k-no-select();
}
.p-animation-index-box { .p-animation-index-box {
.i-animation-index { .i-animation-index {
...@@ -326,6 +278,7 @@ ...@@ -326,6 +278,7 @@
::ng-deep .ant-radio-button-wrapper { ::ng-deep .ant-radio-button-wrapper {
padding: 0 10px; padding: 0 10px;
@include k-no-select();
} }
.i-toolbox { .i-toolbox {
...@@ -356,6 +309,7 @@ ...@@ -356,6 +309,7 @@
cursor: pointer; cursor: pointer;
text-align: left; text-align: left;
display: flex; display: flex;
@include k-no-select();
} }
.i-active { .i-active {
background-color: antiquewhite; background-color: antiquewhite;
......
import { import {
Component, Component,
ElementRef, ElementRef,
...@@ -11,82 +10,6 @@ import { ...@@ -11,82 +10,6 @@ import {
ViewChild ViewChild
} from '@angular/core'; } from '@angular/core';
const editorTpl = `<html lang="en"><head><meta charset="utf-8">
<meta name="viewport"
content="width=device-width,initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no"/>
<style>
@font-face{
font-family: 'BRLNSDB';
src: url("../../../assets/font/BRLNSDB.TTF") ;
}
@font-face{
font-family: 'BRLNSB';
src: url("../../../assets/font/BRLNSB.TTF") ;
}
@font-face{
font-family: 'BRLNSR';
src: url("../../../assets/font/BRLNSR.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: 'GOTHICBI';
src: url("../../../assets/font/GOTHICBI.TTF") ;
}
@font-face{
font-family: 'GOTHICI';
src: url("../../../assets/font/GOTHICI.TTF") ;
}
@font-face{
font-family: 'MMTextBook';
src: url("../../../assets/font/MMTextBook.otf") ;
}
@font-face{
font-family: 'MMTextBook-Bold';
src: url("../../../assets/font/MMTextBook-Bold.otf") ;
}
@font-face{
font-family: 'MMTextBook-BoldItalic';
src: url("../../../assets/font/MMTextBook-BoldItalic.otf") ;
}
@font-face{
font-family: 'MMTextBook-Italic';
src: url("../../../assets/font/MMTextBook-Italic.otf") ;
}
html, body{
/*font-size: 30px;*/
}
body{
height:48px;
overflow: hidden;
margin: 0;
padding: 0 .5rem;
font-family: 'BRLNSB, BRLNSDB, BRLNSR, GOTHIC, GOTHICB, MMTextBook';
background: #FFF;
line-height: 48px;
}
</style>
</head>
<body>{{content}}</body>
</html>`;
@Component({ @Component({
selector: 'app-lesson-title-config', selector: 'app-lesson-title-config',
templateUrl: './lesson-title-config.component.html', templateUrl: './lesson-title-config.component.html',
...@@ -96,45 +19,22 @@ export class LessonTitleConfigComponent implements OnInit, OnChanges, OnDestroy, ...@@ -96,45 +19,22 @@ export class LessonTitleConfigComponent implements OnInit, OnChanges, OnDestroy,
fontFamilyList = [ fontFamilyList = [
'Arial', 'Arial',
'BRLNSB', 'ARBLI'
'BRLNSDB',
'BRLNSR',
'GOTHIC',
'GOTHICB',
// "GOTHICBI",
// "GOTHICI",
'MMTextBook',
// "MMTextBook-Bold",
// "MMTextBook-Italic",
// "MMTextBook-BoldItalic",
]; ];
colorList = [ colorList = [
'#000000', '#111111',
'#ffffff', '#ffffff',
'#595959', '#595959',
'#0075c2', '#0075c2',
'#c61c1e', '#c61c1e',
'#9cbc3a', '#9cbc3a'
'#008000',
'#FF0000',
'#D2691E',
]; ];
MIN_FONT_SIZE = 1; MIN_FONT_SIZE = 1;
MAX_FONT_SIZE = 7; MAX_FONT_SIZE = 7;
isShowFontColorPane = false; isShowFontColorPane = false;
isShowBGColorPane = false; isShowBGColorPane = false;
fontSizeRange = [ fontSizeRange: number[];
// {name: '1号', value: 9},
// {name: '2号', value: 13},
// {name: '3号', value: 16},
// {name: '4号', value: 18},
// {name: '5号', value: 24},
// {name: '6号', value: 32},
];
editorContent = ''; editorContent = '';
...@@ -145,17 +45,25 @@ export class LessonTitleConfigComponent implements OnInit, OnChanges, OnDestroy, ...@@ -145,17 +45,25 @@ export class LessonTitleConfigComponent implements OnInit, OnChanges, OnDestroy,
loopCnt = 0; loopCnt = 0;
maxLoops = 20; maxLoops = 20;
groupIconsCount = {
@ViewChild('titleEl', {static: true}) titleEl: ElementRef; a: Array.from(Array(11).keys()),
b: Array.from(Array(8).keys()),
c: Array.from(Array(8).keys()),
};
prevIcons = [];
prevType = '';
@ViewChild('titleEl') titleEl: ElementRef;
titleEW = null; titleEW = null;
@Input() @Input()
titleObj = { titleObj = {
type: 'a',
content: '', content: '',
icons: [],
audio_url: '' audio_url: ''
}; };
@Input()
withIcon = true;
@Output() @Output()
titleUpdated = new EventEmitter(); titleUpdated = new EventEmitter();
...@@ -176,12 +84,16 @@ export class LessonTitleConfigComponent implements OnInit, OnChanges, OnDestroy, ...@@ -176,12 +84,16 @@ export class LessonTitleConfigComponent implements OnInit, OnChanges, OnDestroy,
let defObj = this.titleObj; let defObj = this.titleObj;
if (!vars.titleObj.currentValue) { if (!vars.titleObj.currentValue) {
defObj = { defObj = {
type: 'a',
content: '', content: '',
icons: [],
audio_url: '' audio_url: ''
}; };
} else { } else {
defObj = vars.titleObj.currentValue; defObj = vars.titleObj.currentValue;
} }
this.titleObj.icons = defObj.icons || [];
this.titleObj.type = defObj.type || 'a';
this.titleObj.content = defObj.content || ''; this.titleObj.content = defObj.content || '';
this.titleObj.audio_url = defObj.audio_url || ''; this.titleObj.audio_url = defObj.audio_url || '';
this.titleEW.document.body.innerHTML = this.titleObj.content; this.titleEW.document.body.innerHTML = this.titleObj.content;
...@@ -190,23 +102,33 @@ export class LessonTitleConfigComponent implements OnInit, OnChanges, OnDestroy, ...@@ -190,23 +102,33 @@ export class LessonTitleConfigComponent implements OnInit, OnChanges, OnDestroy,
ngOnInit() { ngOnInit() {
if (!this.titleObj) { if (!this.titleObj) {
this.titleObj = { this.titleObj = {
type: 'a',
content: '', content: '',
icons: [],
audio_url: '' audio_url: ''
}; };
} }
this.titleObj.icons = this.titleObj.icons || [];
this.titleObj.type = this.titleObj.type || 'a';
this.titleObj.content = this.titleObj.content || ''; this.titleObj.content = this.titleObj.content || '';
this.titleObj.audio_url = this.titleObj.audio_url || ''; this.titleObj.audio_url = this.titleObj.audio_url || '';
this.editorContent = editorTpl.replace('{{content}}', this.titleObj.content) ; this.editorContent = `<html lang="en"><head><meta charset="utf-8">
<meta name="viewport"
content="width=device-width,initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no"/>
</head>
<body style="height:48px;overflow: hidden;margin: 0;padding: 0 .5rem;background: #FFF;line-height: 48px;">
${this.titleObj.content}
</body>
</html>`;
this.titleEW = this.titleEl.nativeElement.contentWindow; this.titleEW = this.titleEl.nativeElement.contentWindow;
console.log('this.titleEW', this.titleEW);
const tdoc = this.titleEW.document; const tdoc = this.titleEW.document;
tdoc.designMode = 'on'; tdoc.designMode = "on";
tdoc.open('text/html', 'replace'); tdoc.open('text/html', 'replace');
tdoc.write(this.editorContent); tdoc.write(this.editorContent);
tdoc.close(); tdoc.close();
tdoc.addEventListener('keypress', this.keyPress, true); tdoc.addEventListener("keypress", this.keyPress, true);
tdoc.addEventListener('blur', () => { tdoc.addEventListener("blur", () => {
if (this.titleObj.content === this.titleEW.document.body.innerHTML.trim()) { if (this.titleObj.content === this.titleEW.document.body.innerHTML.trim()) {
return; return;
} }
...@@ -237,7 +159,30 @@ export class LessonTitleConfigComponent implements OnInit, OnChanges, OnDestroy, ...@@ -237,7 +159,30 @@ export class LessonTitleConfigComponent implements OnInit, OnChanges, OnDestroy,
ngOnDestroy(): void { ngOnDestroy(): void {
} }
iconsChanges(val) {
let a = this.titleObj.icons;
let b = val;
if (a.length > b.length) {
const diff = a.filter(x => !b.includes(x));
const ti = [...this.titleObj.icons];
for (let i = 0; i < diff.length; i++) {
const d = diff[i];
const idx = ti.indexOf(d);
ti.splice(idx, 1);
}
this.titleObj.icons = ti;
} else {
const diff = b.filter(x => !a.includes(x));
this.titleObj.icons = [...this.titleObj.icons, ...diff];
}
this.shouldSave();
}
typeChange(val) {
this.titleObj.icons = [];
this.shouldSave();
}
keyPress(evt) { keyPress(evt) {
try { try {
...@@ -250,9 +195,9 @@ export class LessonTitleConfigComponent implements OnInit, OnChanges, OnDestroy, ...@@ -250,9 +195,9 @@ export class LessonTitleConfigComponent implements OnInit, OnChanges, OnDestroy,
const key = String.fromCharCode(evt.charCode).toLowerCase(); const key = String.fromCharCode(evt.charCode).toLowerCase();
let cmd = ''; let cmd = '';
switch (key) { switch (key) {
case 'b': cmd = 'bold'; break; case 'b': cmd = "bold"; break;
case 'i': cmd = 'italic'; break; case 'i': cmd = "italic"; break;
case 'u': cmd = 'underline'; break; case 'u': cmd = "underline"; break;
} }
...@@ -269,13 +214,10 @@ export class LessonTitleConfigComponent implements OnInit, OnChanges, OnDestroy, ...@@ -269,13 +214,10 @@ export class LessonTitleConfigComponent implements OnInit, OnChanges, OnDestroy,
alert(e); alert(e);
} }
} }
execEditorCommand(command, option?: any) { execEditorCommand(command, option?: any) {
console.log('sssss');
try { try {
this.titleEW.focus(); this.titleEW.focus();
const result = this.titleEW.document.execCommand(command, false, option); this.titleEW.document.execCommand(command, false, option);
console.log(result);
this.loopCnt = 0; this.loopCnt = 0;
return false; return false;
...@@ -287,7 +229,7 @@ export class LessonTitleConfigComponent implements OnInit, OnChanges, OnDestroy, ...@@ -287,7 +229,7 @@ export class LessonTitleConfigComponent implements OnInit, OnChanges, OnDestroy,
}, 100); }, 100);
this.loopCnt += 1; this.loopCnt += 1;
} else { } else {
alert('Error executing command.'); alert("Error executing command.");
} }
} }
} }
...@@ -300,7 +242,7 @@ export class LessonTitleConfigComponent implements OnInit, OnChanges, OnDestroy, ...@@ -300,7 +242,7 @@ export class LessonTitleConfigComponent implements OnInit, OnChanges, OnDestroy,
this.execEditorCommand('forecolor', this.__fontColor); this.execEditorCommand('forecolor', this.__fontColor);
} }
onChangeFontFamily(font) { onChangeFontFamily(font) {
this.execEditorCommand('fontName', font); this.execEditorCommand('fontname', font);
} }
onChangeFontSize(size?: any) { onChangeFontSize(size?: any) {
...@@ -330,10 +272,9 @@ export class LessonTitleConfigComponent implements OnInit, OnChanges, OnDestroy, ...@@ -330,10 +272,9 @@ export class LessonTitleConfigComponent implements OnInit, OnChanges, OnDestroy,
this.titleUpdated.emit(this.titleObj); this.titleUpdated.emit(this.titleObj);
} }
shouldSave = () => { shouldSave = () => {
console.log('title shouldSave', this.titleObj); console.log('title shouldSave');
this.titleObj.content = this.titleEW.document.body.innerHTML.trim(); this.titleObj.content = this.titleEW.document.body.innerHTML.trim();
this.titleUpdated.emit(this.titleObj); this.titleUpdated.emit(this.titleObj);
} }
} }
@import '../../style/common_mixin.css'; @import '../../style/common_mixin';
.cmp-player-content-wrapper{ .cmp-player-content-wrapper{
max-height: 100%; max-height: 100%;
......
...@@ -18,7 +18,7 @@ import { ...@@ -18,7 +18,7 @@ import {
export class PlayerContentWrapperComponent implements OnInit, OnChanges, OnDestroy, AfterViewInit { export class PlayerContentWrapperComponent implements OnInit, OnChanges, OnDestroy, AfterViewInit {
@ViewChild('wrapperEl', {static: true }) wrapperEl: ElementRef; @ViewChild('wrapperEl') wrapperEl: ElementRef;
// // aspect ratio? // // aspect ratio?
@Input() ratio; @Input() ratio;
......
...@@ -5,21 +5,16 @@ ...@@ -5,21 +5,16 @@
[nzAction]="uploadUrl" [nzAction]="uploadUrl"
[nzData]="uploadData" [nzData]="uploadData"
(nzChange)="handleChange($event)"> (nzChange)="handleChange($event)">
<!--[nzBeforeUpload]="customUpload">-->
<div class="p-box d-flex align-items-center"> <div class="p-box d-flex align-items-center">
<div class="p-upload-icon" *ngIf="!picUrl && !uploading"> <div class="p-upload-icon" *ngIf="!picUrl && !uploading">
<i nz-icon nzType="cloud-upload" nzTheme="outline" [style.font-size]="iconSize + 'em'"></i> <i nz-icon type="cloud-upload" theme="outline" [style.font-size]="iconSize + 'em'"></i>
<div class="m-3"></div> <div class="m-3"></div>
<span>{{TIP}}</span> <span>{{TIP}}</span>
<!--<div class="mt-5 p-progress-bar" *ngIf="uploading">-->
<!--<div class="p-progress-bg" [style.width]="progress*0.2+'rem'"></div>-->
<!--<div class="p-progress-value">{{progress}}%</div>-->
<!--</div>-->
</div> </div>
<div class="p-upload-progress-bg" *ngIf="uploading"> <div class="p-upload-progress-bg" *ngIf="uploading">
<div class="i-bg" [style.width]="progress+'%'"></div> <div class="i-bg" [style.width]="progress+'%'"></div>
<div class="i-text"> <div class="i-text">
<fa-icon icon="cloud-upload-alt"></fa-icon> <fa name="cloud-upload"></fa>
Uploading... Uploading...
</div> </div>
</div> </div>
...@@ -32,6 +27,6 @@ ...@@ -32,6 +27,6 @@
nz-popconfirm nzTitle="Are you sure ?" nz-popconfirm nzTitle="Are you sure ?"
(nzOnConfirm)="onDelete()" (nzOnConfirm)="onDelete()"
> >
<i nz-icon nzType="close" nzTheme="outline"></i> <i nz-icon type="close" theme="outline"></i>
</div> </div>
</div> </div>
@import '../../style/common_mixin.css'; @import '../../style/common_mixin';
.p-image-uploader { .p-image-uploader {
position: relative; position: relative;
...@@ -52,15 +52,10 @@ ...@@ -52,15 +52,10 @@
.p-preview { .p-preview {
width: 100%; width: 100%;
height: 100%; height: 100%;
background-size: contain;
background-repeat: no-repeat;
background-position: 50% 50%;
//background-image: url("https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"); //background-image: url("https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png");
@include k-img-bg();
} }
} }
.d-flex{
display: flex;
}
} }
.p-btn-delete { .p-btn-delete {
......
<div class="p-video-box">
<div class="up-video" style="display: flex;"> <div class="up-video" style="display: flex;">
<!--<nz-upload class="" [nzDisabled]="!showUploadBtn"--> <!--<nz-upload class="" [nzDisabled]="!showUploadBtn"-->
<!--[nzShowUploadList]="false"--> <!--[nzShowUploadList]="false"-->
...@@ -16,7 +16,8 @@ ...@@ -16,7 +16,8 @@
<button type="button" nz-button nzType="default" *ngIf="showUploadBtn" [disabled]="uploading" <button type="button" nz-button nzType="default" *ngIf="showUploadBtn" [disabled]="uploading"
[nzLoading]="uploading" > [nzLoading]="uploading" >
<i nz-icon nzType="plus" nzTheme="outline"></i> <i nz-icon type="plus" theme="outline"></i>
<span>{{ uploading ? 'Uploading' : 'Select Video' }}</span> <span>{{ uploading ? 'Uploading' : 'Select Video' }}</span>
<!--<span>Select Video</span>--> <!--<span>Select Video</span>-->
</button> </button>
...@@ -55,9 +56,9 @@ ...@@ -55,9 +56,9 @@
</div> </div>
<div class="p-box d-flex align-items-center p-video-uploader"> <div class="p-box d-flex align-items-center p-video-uploader" style="top: 20px;">
<div class="p-upload-icon" *ngIf="!showUploadBtn && !videoUrl && !uploading"> <div class="p-upload-icon" *ngIf="!showUploadBtn && !videoUrl && !uploading">
<i nz-icon nzType="upload" nzTheme="outline"></i> <i nz-icon type="upload" theme="outline"></i>
<div class="m-3"></div> <div class="m-3"></div>
<span>Click here to upload video</span> <span>Click here to upload video</span>
<div class="mt-5 p-progress-bar" *ngIf="uploading"> <div class="mt-5 p-progress-bar" *ngIf="uploading">
...@@ -69,26 +70,26 @@ ...@@ -69,26 +70,26 @@
[ngClass]="{'smart-bar': showUploadBtn}" > [ngClass]="{'smart-bar': showUploadBtn}" >
<div class="i-bg" [style.width]="progress+'%'"></div> <div class="i-bg" [style.width]="progress+'%'"></div>
<div class="i-text"> <div class="i-text">
<fa-icon icon="cloud-upload-alt"></fa-icon> <fa name="cloud-upload"></fa>
Uploading... Uploading...
</div> </div>
</div> </div>
<div class="p-upload-check-bg" *ngIf="checking"> <div class="p-upload-check-bg" *ngIf="checking">
<div class="i-bg" [style.width]="progress+'%'"></div> <div class="i-bg" [style.width]="progress+'%'"></div>
<div class="i-text"> <div class="i-text">
<fa-icon icon="cloud-upload-alt"></fa-icon> <fa name="cloud-upload"></fa>
<i nz-icon nzType="loading" nzTheme="outline"></i>Checking... <i nz-icon type="loading" theme="outline"></i>Checking...
</div> </div>
</div> </div>
<div class="p-preview" *ngIf="!uploading && videoUrl " > <div class="p-preview" *ngIf="!showUploadBtn && !uploading && videoUrl " >
<!--<video crossorigin="anonymous" [src]="videoUrl" controls #videoNode></video>--> <!--<video crossorigin="anonymous" [src]="videoUrl" controls #videoNode></video>-->
<video [src]="safeVideoUrl(videoUrl)" controls #videoNode></video> <video [src]="safeVideoUrl(videoUrl)" controls #videoNode (loadedmetadata)="videoLoadedMetaData()"></video>
</div> </div>
</div> </div>
<div [style.display]="!checkVideoExists?'none':''"> <div [style.display]="!checkVideoExists?'none':''">
<span><i nz-icon nzType="loading" nzTheme="outline"></i> checking file to upload</span> <span><i nz-icon type="loading" theme="outline"></i> checking file to upload</span>
</div>
</div> </div>
@import '../../style/common_mixin.css'; @import '../../style/common_mixin';
/*.p-video-box{
bottom: 0;
border: 2px dashed #ddd;
border-radius: 0.5rem;
background-color: #fafafa;
text-align: center;
color: #aaa;
padding-top: 56.25%;
//font-size: 4rem;
position: relative;
.p-upload-icon{
left: 50%;
position: absolute;
top: 50%;
transform: translate(-50% ,-50%);
}
}*/
.p-video-uploader { .p-video-uploader {
position: relative; position: relative;
display: block; display: block;
...@@ -33,33 +17,25 @@ ...@@ -33,33 +17,25 @@
background-color: #fafafa; background-color: #fafafa;
text-align: center; text-align: center;
color: #aaa; color: #aaa;
.p-upload-icon {
}
}
.p-upload-icon {
text-align: center; text-align: center;
margin: auto; margin: auto;
.anticon-upload {
}
.p-upload-icon .anticon-upload {
color: #888; color: #888;
font-size: 5rem; font-size: 5rem;
} }
p-progress-bar { .p-progress-bar {
position: relative; position: relative;
width: 20rem; width: 20rem;
height: 1.5rem; height: 1.5rem;
border: 1px solid #ccc; border: 1px solid #ccc;
border-radius: 1rem; border-radius: 1rem;
.p-progress-bg {
}
.p-progress-bar .p-progress-bg {
background-color: #1890ff; background-color: #1890ff;
border-radius: 1rem; border-radius: 1rem;
height: 100%; height: 100%;
} }
.p-progress-bar .p-progress-value { .p-progress-value {
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
...@@ -70,19 +46,21 @@ p-progress-bar { ...@@ -70,19 +46,21 @@ p-progress-bar {
text-align: center; text-align: center;
font-size: 0.9rem; font-size: 0.9rem;
line-height: 1.5rem; line-height: 1.5rem;
} }
.p-preview { }
}
.p-preview {
width: 100%; width: 100%;
height: 100%; height: 100%;
//background-image: url("https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"); //background-image: url("https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png");
video{
}
.p-preview video{
max-height: 100%; max-height: 100%;
max-width: 100%; max-width: 100%;
position: absolute;
display: flex;
} }
}
}
}
.p-btn-delete { .p-btn-delete {
position: absolute; position: absolute;
right: -0.5rem; right: -0.5rem;
......
import {Component, ElementRef, EventEmitter, Input, OnChanges, OnDestroy, Output, SecurityContext, ViewChild} from '@angular/core'; import {Component, ElementRef, EventEmitter, Input, OnChanges, OnDestroy, Output, SecurityContext, ViewChild} from '@angular/core';
import {NzMessageService, UploadChangeParam, UploadFile, UploadFileStatus} from 'ng-zorro-antd'; import {NzMessageService, UploadFile} from 'ng-zorro-antd';
import {DomSanitizer, SafeResourceUrl} from '@angular/platform-browser'; import {DomSanitizer, SafeResourceUrl} from '@angular/platform-browser';
...@@ -24,7 +24,7 @@ export class UploadVideoComponent implements OnChanges, OnDestroy { ...@@ -24,7 +24,7 @@ export class UploadVideoComponent implements OnChanges, OnDestroy {
@Input() @Input()
videoUrl = ''; videoUrl = '';
@ViewChild('videoNode', {static: true }) @ViewChild('videoNode')
videoNode: ElementRef; videoNode: ElementRef;
...@@ -47,8 +47,8 @@ export class UploadVideoComponent implements OnChanges, OnDestroy { ...@@ -47,8 +47,8 @@ export class UploadVideoComponent implements OnChanges, OnDestroy {
item: any; item: any;
// videoItem = null; // videoItem = null;
uploadUrl = (window as any).courseware.uploadUrl(); uploadUrl = (<any> window).courseware.uploadUrl();
uploadData = (window as any).courseware.uploadData(); uploadData = (<any> window).courseware.uploadData();
constructor(private nzMessageService: NzMessageService, constructor(private nzMessageService: NzMessageService,
private sanitization: DomSanitizer private sanitization: DomSanitizer
...@@ -71,7 +71,7 @@ export class UploadVideoComponent implements OnChanges, OnDestroy { ...@@ -71,7 +71,7 @@ export class UploadVideoComponent implements OnChanges, OnDestroy {
} }
safeVideoUrl(url) { safeVideoUrl(url) {
console.log(url); console.log(url)
return this.sanitization.bypassSecurityTrustResourceUrl(url); // `${url}`; return this.sanitization.bypassSecurityTrustResourceUrl(url); // `${url}`;
} }
videoLoadedMetaData() { videoLoadedMetaData() {
...@@ -79,7 +79,7 @@ export class UploadVideoComponent implements OnChanges, OnDestroy { ...@@ -79,7 +79,7 @@ export class UploadVideoComponent implements OnChanges, OnDestroy {
} }
handleChange(info: UploadChangeParam/* { type: string, file: UploadFile, event: any }*/): void { handleChange(info: { type: string, file: UploadFile, event: any }): void {
console.log('info:' , info); console.log('info:' , info);
...@@ -109,7 +109,7 @@ export class UploadVideoComponent implements OnChanges, OnDestroy { ...@@ -109,7 +109,7 @@ export class UploadVideoComponent implements OnChanges, OnDestroy {
break; break;
case 'progress': case 'progress':
this.progress = info.event.percent; this.progress = parseInt(info.event.percent, 10);
this.doProgress(this.progress); this.doProgress(this.progress);
break; break;
} }
...@@ -152,9 +152,9 @@ export class UploadVideoComponent implements OnChanges, OnDestroy { ...@@ -152,9 +152,9 @@ export class UploadVideoComponent implements OnChanges, OnDestroy {
if (duration) { if (duration) {
duration = duration * 1000; duration = duration * 1000;
} }
file.height = height; file['height'] = height;
file.width = width; file['width'] = width;
file.duration = duration; file['duration'] = duration;
vid.preload = 'none'; vid.preload = 'none';
vid.src = ''; vid.src = '';
vid.remove(); vid.remove();
......
@import '../style/common_mixin.css';
.model-content {
width: 100%;
height: 100%;
}
<div class="model-content"> <div class="model-content">
<div class="card-config">
<div *ngFor="let item of dataArray; let i = index" class="card-item" style="padding: 0.5vw;" >
<div style="position: absolute; left: 200px; top: 100px; width: 800px;"> <div class="card-item-content border">
<div class="card-item-content">
<input type="text" nz-input [(ngModel)]="item.text" (blur)="save()"> <div class="title" >
第-<strong>{{ i + 1 }}</strong>-题
<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> </div>
<div class="section" >
<div class="section-title" >
问题
</div>
<div class="section-content">
<div style="flex:1">
<div style="display: flex; margin-bottom: 10px;">
<div style="flex:1">
文字
</div>
<div style="flex:5">
<input type="text" nz-input placeholder="" [(ngModel)]="item.question.text" (blur)="saveItem()" style="width: 250px;" />
</div>
</div>
<div style="display: flex; margin-bottom: 10px;">
<div style="flex:1">
音频
</div>
<div style="flex:5">
<app-audio-recorder [audioUrl]="item.question.audio_url" (audioUploaded)="onAudioUploadSuccessByItem($event, item.question)" ></app-audio-recorder>
</div>
</div>
<!-- div style="position: absolute; left: 1000px; top: 100px; width: 300px;"> <div style="display: flex; margin-bottom: 10px;">
<div style="flex:1">
<input type="text" nz-input [(ngModel)]="item.text_2" (blur)="save()"> 时间
</div>
<app-upload-image-with-preview <div style="flex:5">
[picUrl]="item.pic_url_2" <input type="text" nz-input placeholder="" [(ngModel)]="item.time" (blur)="saveItem()" style="width: 80px;" /><span style="margin-left: 10px;"></span>
(imageUploaded)="onImageUploadSuccess($event, 'pic_url_2')" </div>
></app-upload-image-with-preview> </div>
</div>
<app-audio-recorder
[audioUrl]="item.audio_url_2"
(audioUploaded)="onAudioUploadSuccess($event, 'audio_url_2')"
></app-audio-recorder>
</div -->
</div>
<!--<app-audio-recorder-->
<!--[audioUrl]="item.audio_url"-->
<!--(audioUploaded)="onAudioUploadSuccess($event)"-->
<!--&gt;</app-audio-recorder>-->
<!--<div *ngFor="let it of picArr; let i = index"-->
<!--nz-col nzSpan="8" >-->
<!--<div class="item-box">-->
<!--<app-upload-image-with-preview-->
<!--style="width: 100%"-->
<!--[picUrl]="it.pic_url"-->
<!--(imageUploaded)="onImageUploadSuccessByItem($event, it)"-->
<!--&gt;</app-upload-image-with-preview>-->
<!--<div style="display: flex; justify-items: center; padding-top: 10px">-->
<!--<app-audio-recorder-->
<!--[audioUrl]="it.audio_url"-->
<!--(audioUploaded)="onAudioUploadSuccessByItem($event, it)">-->
<!--</app-audio-recorder>-->
<!--<button style="margin-left: 10px;" nz-button nzType="danger" (click)="deleteItem(it)">-->
<!--<i nz-icon type="close"></i>-->
<!--</button>-->
<!--</div>-->
<!--</div>-->
<!--&lt;!&ndash;<h5> id-{{i+1}} </h5>&ndash;&gt;-->
<!--&lt;!&ndash;<div style="display: flex; align-items: center; justify-content: center; padding-bottom: 5px">&ndash;&gt;-->
<!--&lt;!&ndash;<input type="text" nz-input placeholder="" [(ngModel)]="it.text" (blur)="saveItem()">&ndash;&gt;-->
<!--&lt;!&ndash;<button style="margin-left: 10px;" nz-button nzType="danger" (click)="deleteItem(it)">&ndash;&gt;-->
<!--&lt;!&ndash;<i nz-icon type="close"></i>&ndash;&gt;-->
<!--&lt;!&ndash;</button>&ndash;&gt;-->
<!--&lt;!&ndash;</div>&ndash;&gt;-->
<!--&lt;!&ndash;<app-upload-image-with-preview&ndash;&gt;-->
<!--&lt;!&ndash;style="width: 100%"&ndash;&gt;-->
<!--&lt;!&ndash;[picUrl]="it.pic_url"&ndash;&gt;-->
<!--&lt;!&ndash;(imageUploaded)="onImageUploadSuccessByItem($event, it)"&ndash;&gt;-->
<!--&lt;!&ndash;&gt;</app-upload-image-with-preview>&ndash;&gt;-->
<!--&lt;!&ndash;<div style="display: flex; align-items: center; justify-content: center">&ndash;&gt;-->
<!--&lt;!&ndash;<span style="width: 80px;">&ndash;&gt;-->
<!--&lt;!&ndash;question:&ndash;&gt;-->
<!--&lt;!&ndash;</span>&ndash;&gt;-->
<!--&lt;!&ndash;<app-audio-recorder&ndash;&gt;-->
<!--&lt;!&ndash;[audioUrl]="it['q_audio_url']"&ndash;&gt;-->
<!--&lt;!&ndash;(audioUploaded)="onAudioUploadSuccessByItem($event, it, 'q')">&ndash;&gt;-->
<!--&lt;!&ndash;</app-audio-recorder>&ndash;&gt;-->
<!--&lt;!&ndash;</div>&ndash;&gt;-->
<!--&lt;!&ndash;<div style="display: flex; align-items: center; justify-content: center">&ndash;&gt;-->
<!--&lt;!&ndash;<span style="width: 80px;">&ndash;&gt;-->
<!--&lt;!&ndash;answer:&ndash;&gt;-->
<!--&lt;!&ndash;</span>&ndash;&gt;-->
<!--&lt;!&ndash;<app-audio-recorder&ndash;&gt;-->
<!--&lt;!&ndash;[audioUrl]="it['a_audio_url']"&ndash;&gt;-->
<!--&lt;!&ndash;(audioUploaded)="onAudioUploadSuccessByItem($event, it, 'a')">&ndash;&gt;-->
<!--&lt;!&ndash;</app-audio-recorder>&ndash;&gt;-->
<!--&lt;!&ndash;</div>&ndash;&gt;-->
<!--&lt;!&ndash;<nz-radio-group [(ngModel)]="it.radioValue" >&ndash;&gt;-->
<!--&lt;!&ndash;<label nz-radio nzValue="A" (click)="radioClick(it, 'A')"> <i nz-icon nzType="check" nzTheme="outline"></i> </label>&ndash;&gt;-->
<!--&lt;!&ndash;<label nz-radio nzValue="B" (click)="radioClick(it, 'B')"> <i nz-icon nzType="close" nzTheme="outline"></i> </label>&ndash;&gt;-->
<!--&lt;!&ndash;</nz-radio-group>&ndash;&gt;-->
<!--</div>-->
<!--<div nz-col nzSpan="8" class="add-btn-box" >-->
<!--&lt;!&ndash;<div style="width: 100%; height: 100%;">&ndash;&gt;-->
<!--<button style=" margin: auto; width: 60%; height: 60%;" nz-button nzType="dashed" class="add-btn"-->
<!--(click)="addPic()">-->
<!--<i nz-icon nzType="plus-circle" nzTheme="outline"></i>-->
<!--Add-->
<!--</button>-->
<!--&lt;!&ndash;</div>&ndash;&gt;-->
<!--</div>-->
<!--&lt;!&ndash;<div style="padding-bottom: 30px;">&ndash;&gt;-->
<!--&lt;!&ndash;<h5> title-sound: </h5>&ndash;&gt;-->
<!--&lt;!&ndash;<app-audio-recorder&ndash;&gt;-->
<!--&lt;!&ndash;[audioUrl]="item.contentObj.title_audio_url"&ndash;&gt;-->
<!--&lt;!&ndash;(audioUploaded)="onAudioUploadSuccessByItem($event, item.contentObj, 'title')">&ndash;&gt;-->
<!--&lt;!&ndash;</app-audio-recorder>&ndash;&gt;-->
<!--&lt;!&ndash;</div>&ndash;&gt;-->
<!--&lt;!&ndash;<div style="padding-bottom: 30px;">&ndash;&gt;-->
<!--&lt;!&ndash;<h5> bg-sound: </h5>&ndash;&gt;-->
<!--&lt;!&ndash;<app-audio-recorder&ndash;&gt;-->
<!--&lt;!&ndash;[audioUrl]="item.contentObj.bg_audio_url"&ndash;&gt;-->
<!--&lt;!&ndash;(audioUploaded)="onAudioUploadSuccessByItem($event, item.contentObj, 'bg')">&ndash;&gt;-->
<!--&lt;!&ndash;</app-audio-recorder>&ndash;&gt;-->
<!--&lt;!&ndash;</div>&ndash;&gt;-->
<!--&lt;!&ndash;<span style="margin-bottom: 20px"> 10 : 23 </span>&ndash;&gt;-->
<!--&lt;!&ndash;<div *ngFor="let it of picArr; let i = index">&ndash;&gt;-->
<!--&lt;!&ndash;<span> pic-{{i + 1}}: </span>&ndash;&gt;-->
<!--&lt;!&ndash;<div style="display: flex; align-items: center; padding-bottom: 20px">&ndash;&gt;-->
<!--&lt;!&ndash;<div style="width: 40%; padding-right: 20px">&ndash;&gt;-->
<!--&lt;!&ndash;<app-upload-image-with-preview&ndash;&gt;-->
<!--&lt;!&ndash;[picUrl]="it.pic_url"&ndash;&gt;-->
<!--&lt;!&ndash;(imageUploaded)="onImageUploadSuccessByItem($event, it)"&ndash;&gt;-->
<!--&lt;!&ndash;&gt;</app-upload-image-with-preview>&ndash;&gt;-->
<!--&lt;!&ndash;</div>&ndash;&gt;-->
<!--&lt;!&ndash;<div class="pic-sound-box">&ndash;&gt;-->
<!--&lt;!&ndash;<div nz-row style="width: 50%; padding-bottom: 20px;">&ndash;&gt;-->
<!--&lt;!&ndash;<div *ngFor="let it2 of it.soundArr; let i2 = index" nz-col nzSpan="8">&ndash;&gt;-->
<!--&lt;!&ndash;<label nz-checkbox nzValue="{{'answer_' + (i2 + 1)}}" [(ngModel)]="it2.answer" (ngModelChange)="clickCheckBox()" >{{i2 + 1}}</label>&ndash;&gt;-->
<!--&lt;!&ndash;</div>&ndash;&gt;-->
<!--&lt;!&ndash;</div>&ndash;&gt;-->
<!--&lt;!&ndash;<div *ngFor="let it2 of it.soundArr; let i2 = index" style="display: flex; align-items: center; padding-bottom: 5px">&ndash;&gt;-->
<!--&lt;!&ndash;<span style="padding-right: 10px">sound-{{i2 + 1}}:</span>&ndash;&gt;-->
<!--&lt;!&ndash;<app-audio-recorder&ndash;&gt;-->
<!--&lt;!&ndash;[audioUrl]="it2.audio_url"&ndash;&gt;-->
<!--&lt;!&ndash;(audioUploaded)="onAudioUploadSuccessByItem($event, it2)">&ndash;&gt;-->
<!--&lt;!&ndash;</app-audio-recorder>&ndash;&gt;-->
<!--&lt;!&ndash;</div>&ndash;&gt;-->
<!--&lt;!&ndash;</div>&ndash;&gt;-->
<!--&lt;!&ndash;</div>&ndash;&gt;-->
<!--&lt;!&ndash;</div>&ndash;&gt;-->
<!--&lt;!&ndash;<div style="margin: auto; width: 95%;" nz-row nzType="flex" nzJustify="start" nzAlign="middle">&ndash;&gt;-->
<!--&lt;!&ndash;<div *ngFor="let it of picArr; let i = index"&ndash;&gt;-->
<!--&lt;!&ndash;nz-col nzSpan="8" >&ndash;&gt;-->
<!--&lt;!&ndash;<div class="item-box">&ndash;&gt;-->
<!--&lt;!&ndash;<h5> id-{{i+1}} </h5>&ndash;&gt;-->
<!--&lt;!&ndash;<div style="display: flex; align-items: center; justify-content: center; padding-bottom: 5px">&ndash;&gt;-->
<!--&lt;!&ndash;<input type="text" nz-input placeholder="" [(ngModel)]="it.text" (blur)="saveItem()">&ndash;&gt;-->
<!--&lt;!&ndash;<button style="margin-left: 10px;" nz-button nzType="danger" (click)="deleteItem(it)">&ndash;&gt;-->
<!--&lt;!&ndash;<i nz-icon type="close"></i>&ndash;&gt;-->
<!--&lt;!&ndash;</button>&ndash;&gt;-->
<!--&lt;!&ndash;</div>&ndash;&gt;-->
<!--&lt;!&ndash;<app-upload-image-with-preview&ndash;&gt;-->
<!--&lt;!&ndash;style="width: 100%"&ndash;&gt;-->
<!--&lt;!&ndash;[picUrl]="it.pic_url"&ndash;&gt;-->
<!--&lt;!&ndash;(imageUploaded)="onImageUploadSuccessByItem($event, it)"&ndash;&gt;-->
<!--&lt;!&ndash;&gt;</app-upload-image-with-preview>&ndash;&gt;-->
<!--&lt;!&ndash;<div style="display: flex; align-items: center; justify-content: center">&ndash;&gt;-->
<!--&lt;!&ndash;<span style="width: 80px;">&ndash;&gt;-->
<!--&lt;!&ndash;question:&ndash;&gt;-->
<!--&lt;!&ndash;</span>&ndash;&gt;-->
<!--&lt;!&ndash;<app-audio-recorder&ndash;&gt;-->
<!--&lt;!&ndash;[audioUrl]="it['q_audio_url']"&ndash;&gt;-->
<!--&lt;!&ndash;(audioUploaded)="onAudioUploadSuccessByItem($event, it, 'q')">&ndash;&gt;-->
<!--&lt;!&ndash;</app-audio-recorder>&ndash;&gt;-->
<!--&lt;!&ndash;</div>&ndash;&gt;-->
<!--&lt;!&ndash;<div style="display: flex; align-items: center; justify-content: center">&ndash;&gt;-->
<!--&lt;!&ndash;<span style="width: 80px;">&ndash;&gt;--> <div style="flex:1">
<!--&lt;!&ndash;answer:&ndash;&gt;--> <div style="display: flex; ">
<!--&lt;!&ndash;</span>&ndash;&gt;--> <div style="flex:1">
<!--&lt;!&ndash;<app-audio-recorder&ndash;&gt;--> 图片
<!--&lt;!&ndash;[audioUrl]="it['a_audio_url']"&ndash;&gt;--> </div>
<!--&lt;!&ndash;(audioUploaded)="onAudioUploadSuccessByItem($event, it, 'a')">&ndash;&gt;--> <div style="flex:5">
<!--&lt;!&ndash;</app-audio-recorder>&ndash;&gt;--> <app-upload-image-with-preview style="width: 100%;" [picUrl]="item.question.image_url" (imageUploaded)="onImageUploadSuccessByItem($event, item.question)"></app-upload-image-with-preview>
</div>
</div>
</div>
</div>
</div>
<!--&lt;!&ndash;</div>&ndash;&gt;-->
<div class="section" >
<div class="section-title" >
正确选项
</div>
<div class="section-content">
<div style="flex:10">
<div style="display: flex; margin-bottom: 10px;">
<div style="flex:1">
文字
</div>
<div style="flex:5">
<input type="text" nz-input placeholder="" [(ngModel)]="item.choice.correct.text" (blur)="saveItem()" />
</div>
</div>
<div style="display: flex; margin-bottom: 10px;">
<div style="flex:1">
图片
</div>
<div style="flex:5">
<app-upload-image-with-preview style="width: 100%;" [picUrl]="item.choice.correct.image_url" (imageUploaded)="onImageUploadSuccessByItem($event, item.choice.correct)"></app-upload-image-with-preview>
</div>
</div>
<!--&lt;!&ndash;<nz-radio-group [(ngModel)]="it.radioValue" >&ndash;&gt;--> <div style="display: flex; ">
<!--&lt;!&ndash;<label nz-radio nzValue="A" (click)="radioClick(it, 'A')"> <i nz-icon nzType="check" nzTheme="outline"></i> </label>&ndash;&gt;--> <div style="flex:2">
<!--&lt;!&ndash;<label nz-radio nzValue="B" (click)="radioClick(it, 'B')"> <i nz-icon nzType="close" nzTheme="outline"></i> </label>&ndash;&gt;--> 显示选项
<!--&lt;!&ndash;</nz-radio-group>&ndash;&gt;--> </div>
<div style="flex:4">
<nz-radio-group [(ngModel)]="item.choice.correct.isText" (ngModelChange)="saveItem()" >
<label nz-radio [nzValue]="true">文字</label>
<label nz-radio [nzValue]="false">图片</label>
</nz-radio-group>
</div>
</div>
</div>
<div style="flex:20">
<!--&lt;!&ndash;</div>&ndash;&gt;--> </div>
</div>
</div>
<div class="section" >
<div class="section-title" >
错误选项
<div *ngIf="item.choice.incorrect.length<3" style="text-align: center; float: right;">
<button nz-button nzType="primary" (click)="addChoice(i)" >
<i nz-icon nzType="plus-circle" nzTheme="outline"></i>
添加
</button>
</div>
</div>
<div class="section-content">
<div *ngFor="let choiceItem of item.choice.incorrect; let choiceIndex = index" style="width:220px; height:230px; border:1px solid #CCC; padding:5px; border-radius: 5px; margin: 0 10px;">
<div style="display: flex; margin-bottom: 10px;">
<div style="flex:1">
文字
</div>
<div style="flex:5">
<input type="text" nz-input placeholder="" [(ngModel)]="choiceItem.text" (blur)="saveItem()" />
</div>
</div>
<!--&lt;!&ndash;</div>&ndash;&gt;--> <div style="display: flex; margin-bottom: 10px;">
<div style="flex:1">
图片
</div>
<div style="flex:5">
<app-upload-image-with-preview style="width: 100%;" [picUrl]="choiceItem.image_url" (imageUploaded)="onImageUploadSuccessByItem($event, choiceItem)"></app-upload-image-with-preview>
</div>
</div>
<div style="display: flex; ">
<div style="flex:2">
显示选项
</div>
<div style="flex:4">
<nz-radio-group [(ngModel)]="choiceItem.isText" (ngModelChange)="saveItem()" >
<label nz-radio [nzValue]="true">文字</label>
<label nz-radio [nzValue]="false">图片</label>
</nz-radio-group>
</div>
</div>
<div *ngIf="choiceIndex>0" style="text-align: center;">
<button style="margin-top: 10px;" nz-button nzType="danger" (click)="deleteChoice(i, choiceIndex)" >
<span>删除此选项</span>
</button>
</div>
</div>
</div>
</div>
<div class="section" >
<div style="text-align: right; padding-right: 20px;">
<button style="margin-bottom: 10px;" nz-button nzType="danger" (click)="deleteItem(i)" >
<span>删除本题</span>
</button>
</div>
</div>
</div>
</div>
</div>
<div class="card-item" style="padding: 0.5vw;" >
<button nz-button nzType="primary" class="add-btn" (click)="addItem()">
<i nz-icon nzType="plus-circle" nzTheme="outline"></i>
新建卡片组
</button>
</div>
</div>
<!--</div>--> </div>
@import "../style/common_mixin";
.model-content {
.card-config {
width: 100%;
height: 100%;
margin-left: 10px;
display: flex;
flex-wrap: wrap;
.card-item{
flex:1;
.border {
border-radius: 20px;
border-style: dashed;
padding:20px;
width: 800px;
}
.card-item-content{
.title {
font-size: 24px;
width: 100%;
text-align: center;
}
.section{
border-top: 1px solid ;
padding: 10px 0;
.section-title{
font-size: 24px;
width: 100%;
}
.section-content{
display: flex;
margin: 5px 0 10px 0;
}
}
.pic-sound-box {
width: 50%;
display: flex;
flex-direction: column;
}
.add-btn-box {
display: flex;
align-items: center;
justify-content: center;
height: 20vw;
padding: 10px;
padding-top: 5vw;
}
}
}
}
}
import {Component, EventEmitter, Input, OnDestroy, OnChanges, OnInit, Output, ApplicationRef} from '@angular/core'; import {Component, EventEmitter, Input, OnDestroy, OnChanges, OnInit, Output, ApplicationRef} from '@angular/core';
@Component({ @Component({
selector: 'app-form', selector: 'app-form',
templateUrl: './form.component.html', templateUrl: './form.component.html',
styleUrls: ['./form.component.css'] styleUrls: ['./form.component.scss']
}) })
export class FormComponent implements OnInit, OnChanges, OnDestroy { export class FormComponent implements OnInit, OnChanges, OnDestroy {
// 储存数据用 dataArray = [];
saveKey = "test_0011"; _item: any;
// 储存对象 KEY = 'DataKey_MultipleChoice';
item;
set item(item) {
this._item = item;
}
get item() {
return this._item;
}
@Output()
update = new EventEmitter();
constructor(private appRef: ApplicationRef) { constructor(private appRef: ApplicationRef) {
} }
ngOnInit() { ngOnInit() {
this.item = {}; this.item = {};
this.item.contentObj = {};
// 获取存储的数据 const getData = (<any> window).courseware.getData;
(<any> window).courseware.getData((data) => { getData((data) => {
if (data) { if (data) {
this.item = data; this.item = data;
} else {
this.item = {};
}
if ( !this.item.contentObj ) {
this.item.contentObj = {};
} }
this.init(); this.init();
this.refresh(); this.refresh();
this.saveItem();
}, this.KEY);
}
ngOnChanges() {
}, this.saveKey); }
ngOnDestroy() {
} }
ngOnChanges() { init() {
if (this.item.contentObj.dataArray) {
this.dataArray = this.item.contentObj.dataArray;
} else {
this.dataArray = this.getDefaultPicArr();
this.item.contentObj.dataArray = this.dataArray;
}
} }
ngOnDestroy() { cardItemData(){
return {
time: 3,
question:{
text:"",
image_url:"",
audio_url:""
},
choice:{
correct:{ isText: true, text: "", image_url: "" },
incorrect:[
{ isText: true, text: "", image_url: "" },
{ isText: true, text: "", image_url: "" },
{ isText: true, text: "", image_url: "" },
]
}
};
} }
cardChoiceData(){
return { isText: true, text: "", image_url: "" }
}
getDefaultPicArr() {
let arr = [];
return arr;
}
init() {
initData() {
} }
deleteItem(index) {
if (index !== -1) {
this.dataArray.splice(index, 1);
}
this.save();
}
/** deleteChoice(questionIndex, choiceIndex){
* 储存图片数据 if (questionIndex !== -1 && choiceIndex !== -1) {
* @param e this.dataArray[questionIndex].choice.incorrect.splice(choiceIndex, 1);
*/ }
onImageUploadSuccess(e, key) {
this.item[key] = e.url;
this.save(); this.save();
} }
addChoice(questionIndex) {
let item = this.cardChoiceData();
this.dataArray[questionIndex].choice.incorrect.push(item);
this.saveItem();
}
/** onImageUploadSuccessByItem(e, item) {
* 储存音频数据 item.image_url = e.url
* @param e this.save();
*/ }
onAudioUploadSuccess(e, key) {
this.item[key] = e.url; onAudioUploadSuccessByItem(e, item) {
item.audio_url = e.url;
this.save(); this.save();
} }
onTitleAudioUploadSuccess(e) {
this.item.contentObj.titleAudio_url = e.url;
this.save();
}
addItem() {
let item = this.cardItemData();
this.dataArray.push(item);
this.saveItem();
}
radioClick(it, radioValue) {
it.radioValue = radioValue;
this.saveItem();
}
clickCheckBox() {
this.saveItem();
}
saveItem() {
this.save();
}
/**
* 储存数据
*/
save() { save() {
(<any> window).courseware.setData(this.item, null, this.saveKey); (<any> window).courseware.setData(this.item, null, this.KEY);
this.refresh(); this.refresh();
console.log(this.item)
} }
/**
* 刷新 渲染页面
*/
refresh() { refresh() {
setTimeout(() => { setTimeout(() => {
this.appRef.tick(); this.appRef.tick();
......
import {
MySprite,
getMinScale,
ShapeRect,
tweenChange,
randomSortByArr,
Label,
showPopParticle,
moveItem,
removeItemFromArr,
rotateItem,
ShapeRectNew
} from "./Unit";
import { CartoonAnimation } from './CartoonAnimation'
export class Cartoon {
// 系统缩放比例
mapScale = 1;
stageWidth;
stageHeight;
clientWidth;
clientHeight;
// 音乐 和 图片的缓冲区
audio = new Map();
images = new Map();
imagesOriginSize = new Map();
// 坐标原点 包含缩放
originX = 0;
originY = 0;
setOrigin = (x, y)=>{
this.originX = x;
this.originY = y;
}
getOrigin = ()=>{
return {
x: this.originX,
y: this.originY
}
}
// 相对坐标原点 包含缩放 用于添加孩子动画元素
relativeOriginX = 0;
relativeOriginY = 0;
setRelativeOrigin = (x, y)=>{
this.relativeOriginX = x;
this.relativeOriginY = y;
}
getRelativeOrigin = ()=>{
return {
x: this.relativeOriginX,
y: this.relativeOriginY
}
}
// 存放音乐和图片的地址
audioObj = {}
imageObj = {}
_currentPlayAudio;
// 添加音乐
addAudio = ( key, url ) => {
this.audioObj[key] = url
};
// 添加音乐
addImage = ( key, url ) => {
this.imageObj[key] = url
};
// 播放音乐
_playingNow = []
playAudio = function( key, now = false, callback = null) {
const audio = this.audio.get(key);
if (audio) {
if (now) {
audio.pause();
audio.currentTime = 0;
}
if (callback) {
audio.onended = () => {
let index = this._playingNow.indexOf(audio)
if(index != -1){
this._playingNow.splice(index,1)
}
callback();
};
}
audio.play();
audio.callback = callback
this._playingNow.push(audio)
this._currentPlayAudio = audio;
}
}
stopAllAudio(){
this._playingNow.forEach(audio=>{
audio.onended && audio.onended()
audio.pause();
audio.currentTime = 0;
})
this._playingNow = []
}
stopAudio(){
if(this._currentPlayAudio){
this._currentPlayAudio.pause();
this._currentPlayAudio.currentTime = 0;
}
}
// 异步加载图片 音频资源
loadResources = ()=> {
const pr = [];
for(let key in this.imageObj){
const p = this.preloadImage(this.imageObj[key]).then( (img:any) => {
this.images.set(key, img);
this.imagesOriginSize.set(key, {width:img.width, height:img.height});
}).catch(err => console.log(key));
pr.push(p);
};
for(let key in this.audioObj){
const a = this.preloadAudio(this.audioObj[key]).then( (audio:any) => {
this.audio.set(key, audio);
}).catch(err => console.log(key));
pr.push(a);
};
return Promise.all(pr);
}
// 预加载图片
preloadImage = (url) => {
return new Promise((resolve, reject) => {
const img = new Image();
img.src = url;
img.onload = () => resolve(img);
img.onerror = reject;
img.src = url;
});
}
// 预加载音频
preloadAudio = (url) => {
return new Promise((resolve, reject) => {
const audio = new Audio();
audio.oncanplay = a => {
resolve(audio);
};
audio.onerror = () => {
reject();
};
audio.src = url;
audio.load();
});
}
// 缓存页面元素
cartoonElementsBuffer = {}
createCartoonElement = (key, type, width?:number, heigth?:number, initX?:number, initY?:number)=>{
this.cartoonElementsBuffer[key] = {}
this.cartoonElementsBuffer[key].id = key;
this.cartoonElementsBuffer[key].width = width;
this.cartoonElementsBuffer[key].heigth = heigth;
this.cartoonElementsBuffer[key].width = width;
this.cartoonElementsBuffer[key].heigth = heigth;
this.cartoonElementsBuffer[key].x = initX;
this.cartoonElementsBuffer[key].y = initY;
switch(type){
case "MySprite": this.cartoonElementsBuffer[key].ref = new MySprite(); break;
case "ShapeRect": this.cartoonElementsBuffer[key].ref = new ShapeRect(); break;
case "ShapeRectNew": this.cartoonElementsBuffer[key].ref = new ShapeRectNew(); break;
case "Label": this.cartoonElementsBuffer[key].ref = new Label(); break;
default : this.cartoonElementsBuffer[key].ref = new MySprite(); break;
}
this.cartoonElementsBuffer[key].ref.x = initX;
this.cartoonElementsBuffer[key].ref.y = initY;
this.cartoonElementsBuffer[key].animation = new CartoonAnimation(this.cartoonElementsBuffer[key].ref)
return this.cartoonElementsBuffer[key]
}
createElement = (type, initX?:number, initY?:number)=>{
let element;
switch(type){
case "MySprite": element = new MySprite(); break;
case "ShapeRect": element = new ShapeRect(); break;
default : element = new MySprite(); break;
}
element.x = initX;
element.y = initY;
return element
}
createLabel = ({
text,
fontName,
fontColor,
fontSize,
textAlign
}, initX?, initY?)=>{
let element = new Label()
element.text = text;
element.fontName = fontName;
element.fontColor = fontColor;
element.fontSize = fontSize;
element.textAlign = textAlign;
element.x = initX;
element.y = initY;
return element
}
getCartoonElementRef = (key)=>{
if(this.cartoonElementsBuffer[key]){
return this.cartoonElementsBuffer[key].ref;
}else{
return undefined
}
}
getCartoonElement = (key)=>{
return this.cartoonElementsBuffer[key]
}
setCartoonElementPosition = (key, posi)=>{
this.cartoonElementsBuffer[key].ref.x = posi.x
this.cartoonElementsBuffer[key].ref.y = posi.y
this.cartoonElementsBuffer[key].x = posi.x
this.cartoonElementsBuffer[key].y = posi.y
}
setCartoonElementPositionX = (key, x)=>{
this.cartoonElementsBuffer[key].ref.x = x
this.cartoonElementsBuffer[key].x = x
}
setCartoonElementRelativePositionX = (key, x)=>{
this.cartoonElementsBuffer[key].relativeX = x
}
setCartoonElementPositionY = (key, y)=>{
this.cartoonElementsBuffer[key].ref.y = y
this.cartoonElementsBuffer[key].y = y
}
setCartoonElementRelativePositionY = (key, y)=>{
this.cartoonElementsBuffer[key].relativeY = y
}
getCartoonElementPosition = (key)=>{
return {
x: this.cartoonElementsBuffer[key].x,
y: this.cartoonElementsBuffer[key].y
}
}
getCartoonElementRelativePosition = (key)=>{
return {
x: this.cartoonElementsBuffer[key].relativeX,
y: this.cartoonElementsBuffer[key].relativeY
}
}
animation = new CartoonAnimation(this)
}
\ No newline at end of file
import {
MySprite,
getMinScale,
ShapeRect,
tweenChange,
randomSortByArr,
Label,
showPopParticle,
moveItem,
removeItemFromArr,
rotateItem
} from "./Unit";
export class CartoonAnimation {
animations = {}
currentRef;
constructor(ref){
this.currentRef = ref
}
add(key:string, func:Function){
this.animations[key] = func
}
move = (ele, distPosition, runTime, callback?:Function )=>{
tweenChange(ele, distPosition, runTime, ()=>{
callback && callback()
});
}
scaleChange = this.move;
run( key:string ){
this.animations[key]()
}
overCard = function (callback?: any){
let w = this.currentRef.scaleX;
this.scaleChange(this.currentRef, { scaleX: 0 }, 0.3, () => {
this.currentRef.frontSide.visible = !this.currentRef.frontSide.visible;
this.currentRef.backSide.visible = !this.currentRef.backSide.visible;
this.scaleChange(this.currentRef, { scaleX: w }, 0.3, () => {
callback && callback();
});
});
}
}
\ No newline at end of file
import TWEEN from "@tweenjs/tween.js";
import TWEEN from '@tweenjs/tween.js'; import { del } from "selenium-webdriver/http";
import construct = Reflect.construct;
interface AirWindow extends Window {
air: any;
curCtx: any;
}
declare let window: AirWindow;
class Sprite { class Sprite {
x = 0; x = 0;
y = 0; y = 0;
color = ''; color = "";
radius = 0; radius = 0;
alive = false; alive = false;
margin = 0; margin = 0;
angle = 0; angle = 0;
ctx; ctx;
id;
constructor(ctx = null) { constructor(ctx = null) {
if (!ctx) { if (!ctx) {
this.ctx = window.curCtx; this.ctx = window["curCtx"];
} else { } else {
this.ctx = ctx; this.ctx = ctx;
} }
...@@ -27,18 +22,10 @@ class Sprite { ...@@ -27,18 +22,10 @@ class Sprite {
update($event) { update($event) {
this.draw(); this.draw();
} }
draw() { draw() {}
}
} }
export class MySprite extends Sprite { export class MySprite extends Sprite {
_width = 0; _width = 0;
_height = 0; _height = 0;
_anchorX = 0; _anchorX = 0;
...@@ -53,14 +40,6 @@ export class MySprite extends Sprite { ...@@ -53,14 +40,6 @@ export class MySprite extends Sprite {
skewX = 0; skewX = 0;
skewY = 0; skewY = 0;
_shadowFlag = false;
_shadowColor;
_shadowOffsetX = 0;
_shadowOffsetY = 0;
_shadowBlur = 5;
_radius = 0;
children = [this]; children = [this];
childDepandVisible = true; childDepandVisible = true;
...@@ -69,50 +48,26 @@ export class MySprite extends Sprite { ...@@ -69,50 +48,26 @@ export class MySprite extends Sprite {
img; img;
_z = 0; _z = 0;
init(imgObj = null, anchorX: number = 0.5, anchorY: number = 0.5) { init(imgObj = null, anchorX: number = 0.5, anchorY: number = 0.5) {
if (imgObj) { if (imgObj) {
this.img = imgObj; this.img = imgObj;
this.width = this.img.width; this.width = this.img.width;
this.height = this.img.height; this.height = this.img.height;
} }
this.anchorX = anchorX; this.anchorX = anchorX;
this.anchorY = anchorY; this.anchorY = anchorY;
} }
setShadow(offX, offY, blur, color = 'rgba(0, 0, 0, 0.3)') {
this._shadowFlag = true;
this._shadowColor = color;
this._shadowOffsetX = offX;
this._shadowOffsetY = offY;
this._shadowBlur = blur;
}
setRadius(r) {
this._radius = r;
}
update($event = null) { update($event = null) {
if (!this.visible && this.childDepandVisible) { if (!this.visible && this.childDepandVisible) {
return; return;
} }
this.draw(); this.draw();
} }
draw() { draw() {
this.ctx.save(); this.ctx.save();
this.drawInit(); this.drawInit();
...@@ -120,95 +75,49 @@ export class MySprite extends Sprite { ...@@ -120,95 +75,49 @@ export class MySprite extends Sprite {
this.updateChildren(); this.updateChildren();
this.ctx.restore(); this.ctx.restore();
} }
drawInit() { drawInit() {
this.ctx.translate(this.x, this.y); this.ctx.translate(this.x, this.y);
this.ctx.rotate(this.rotation * Math.PI / 180); this.ctx.rotate((this.rotation * Math.PI) / 180);
this.ctx.scale(this.scaleX, this.scaleY); this.ctx.scale(this.scaleX, this.scaleY);
this.ctx.globalAlpha = this.alpha; this.ctx.globalAlpha = this.alpha;
this.ctx.transform(1, this.skewX, this.skewY, 1, 0, 0); this.ctx.transform(1, this.skewX, this.skewY, 1, 0, 0);
if (this._radius) {
const r = this._radius;
const w = this.width;
const h = this.height;
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);
this.ctx.clip();
}
} }
drawSelf() { 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.img) { if (this.img) {
this.ctx.drawImage(this.img, this._offX, this._offY); this.ctx.drawImage(this.img, this._offX, this._offY);
} }
} }
updateChildren() { updateChildren() {
if (this.children.length <= 0) {
return;
}
if (this.children.length <= 0) { return; } for (let i = 0; i < this.children.length; i++) {
if (this.children[i] === this) {
for (const child of this.children) {
if (child === this) {
if (this.visible) { if (this.visible) {
this.drawSelf(); this.drawSelf();
} }
} else { } else {
child.update(); this.children[i].update();
} }
} }
} }
load(url, anchorX = 0.5, anchorY = 0.5) { load(url, anchorX = 0.5, anchorY = 0.5) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const img = new Image(); const img = new Image();
img.onload = () => resolve(img); img.onload = () => resolve(img);
img.onerror = reject; img.onerror = reject;
img.src = url; img.src = url;
}).then(img => { }).then(img => {
this.init(img, anchorX, anchorY); this.init(img, anchorX, anchorY);
return img; return img;
}); });
...@@ -225,11 +134,9 @@ export class MySprite extends Sprite { ...@@ -225,11 +134,9 @@ export class MySprite extends Sprite {
return a._z - b._z; return a._z - b._z;
}); });
if (this.childDepandAlpha) { if (this.childDepandAlpha) {
child.alpha = this.alpha; child.alpha = this.alpha;
} }
} }
removeChild(child) { removeChild(child) {
const index = this.children.indexOf(child); const index = this.children.indexOf(child);
...@@ -241,18 +148,18 @@ export class MySprite extends Sprite { ...@@ -241,18 +148,18 @@ export class MySprite extends Sprite {
removeChildren() { removeChildren() {
for (let i = 0; i < this.children.length; i++) { for (let i = 0; i < this.children.length; i++) {
if (this.children[i]) { if (this.children[i]) {
if (this.children[i] !== this) { if (this.children[i] != this) {
this.children.splice(i, 1); this.children.splice(i, 1);
i --; i--;
} }
} }
} }
} }
_changeChildAlpha(alpha) { _changeChildAlpha(alpha) {
for (const child of this.children) { for (let i = 0; i < this.children.length; i++) {
if (child !== this) { if (this.children[i] != this) {
child.alpha = alpha; this.children[i].alpha = alpha;
} }
} }
} }
...@@ -304,10 +211,7 @@ export class MySprite extends Sprite { ...@@ -304,10 +211,7 @@ export class MySprite extends Sprite {
} }
getBoundingBox() { getBoundingBox() {
const getParentData = item => {
const getParentData = (item) => {
let px = item.x; let px = item.x;
let py = item.y; let py = item.y;
...@@ -315,7 +219,6 @@ export class MySprite extends Sprite { ...@@ -315,7 +219,6 @@ export class MySprite extends Sprite {
let sy = item.scaleY; let sy = item.scaleY;
const parent = item.parent; const parent = item.parent;
if (parent) { if (parent) {
const obj = getParentData(parent); const obj = getParentData(parent);
const _x = obj.px; const _x = obj.px;
...@@ -328,16 +231,13 @@ export class MySprite extends Sprite { ...@@ -328,16 +231,13 @@ export class MySprite extends Sprite {
sx *= _sx; sx *= _sx;
sy *= _sy; sy *= _sy;
} }
return {px, py, sx, sy}; return { px, py, sx, sy };
}; };
const data = getParentData(this); const data = getParentData(this);
const x = data.px + this._offX * Math.abs(data.sx); const x = data.px + this._offX * Math.abs(data.sx);
const y = data.py + this._offY * Math.abs(data.sy); const y = data.py + this._offY * Math.abs(data.sy);
const width = this.width * Math.abs(data.sx); const width = this.width * Math.abs(data.sx);
...@@ -348,40 +248,29 @@ export class MySprite extends Sprite { ...@@ -348,40 +248,29 @@ export class MySprite extends Sprite {
// const width = this.width * Math.abs(this.scaleX); // const width = this.width * Math.abs(this.scaleX);
// const height = this.height * Math.abs(this.scaleY); // const height = this.height * Math.abs(this.scaleY);
return {x, y, width, height}; return { x, y, width, height };
} }
} }
export class ColorSpr extends MySprite { export class ColorSpr extends MySprite {
r = 0; r = 0;
g = 0; g = 0;
b = 0; b = 0;
createGSCanvas() { createGSCanvas() {
if (!this.img) { if (!this.img) {
return; return;
} }
const rect = this.getBoundingBox(); const rect = this.getBoundingBox();
if (rect.width <= 1 || rect.height <= 1) { if (rect.width <= 1 || rect.height <= 1) {
return; return;
} }
const c = this.ctx.getImageData(rect.x, rect.y, rect.width, rect.height); const c = this.ctx.getImageData(rect.x, rect.y, rect.width, rect.height);
for ( let i = 0; i < c.height; i++) { for (let i = 0; i < c.height; i++) {
for ( let j = 0; j < c.width; j++) { for (let j = 0; j < c.width; j++) {
const x = i * 4 * c.width + j * 4;
const x = (i * 4) * c.width + ( j * 4 );
const r = c.data[x]; const r = c.data[x];
const g = c.data[x + 1]; const g = c.data[x + 1];
const b = c.data[x + 2]; const b = c.data[x + 2];
...@@ -390,8 +279,6 @@ export class ColorSpr extends MySprite { ...@@ -390,8 +279,6 @@ export class ColorSpr extends MySprite {
c.data[x + 1] = this.g; c.data[x + 1] = this.g;
c.data[x + 2] = this.b; c.data[x + 2] = this.b;
// c.data[x] = c.data[x + 1] = c.data[x + 2] = (r + g + b) / 3 ; // c.data[x] = c.data[x + 1] = c.data[x + 2] = (r + g + b) / 3 ;
// // c.data[x + 3] = 255; // // c.data[x + 3] = 255;
} }
...@@ -400,34 +287,26 @@ export class ColorSpr extends MySprite { ...@@ -400,34 +287,26 @@ export class ColorSpr extends MySprite {
this.ctx.putImageData(c, rect.x, rect.y, 0, 0, rect.width, rect.height); this.ctx.putImageData(c, rect.x, rect.y, 0, 0, rect.width, rect.height);
} }
drawSelf() { drawSelf() {
super.drawSelf(); super.drawSelf();
this.createGSCanvas(); this.createGSCanvas();
} }
} }
export class GrayscaleSpr extends MySprite { export class GrayscaleSpr extends MySprite {
grayScale = 120; grayScale = 120;
createGSCanvas() { createGSCanvas() {
if (!this.img) { if (!this.img) {
return; return;
} }
const rect = this.getBoundingBox(); const rect = this.getBoundingBox();
const c = this.ctx.getImageData(rect.x, rect.y, rect.width, rect.height); const c = this.ctx.getImageData(rect.x, rect.y, rect.width, rect.height);
for ( let i = 0; i < c.height; i++) { for (let i = 0; i < c.height; i++) {
for ( let j = 0; j < c.width; j++) { for (let j = 0; j < c.width; j++) {
const x = i * 4 * c.width + j * 4;
const x = (i * 4) * c.width + ( j * 4 );
const r = c.data[x]; const r = c.data[x];
const g = c.data[x + 1]; const g = c.data[x + 1];
const b = c.data[x + 2]; const b = c.data[x + 2];
...@@ -441,37 +320,26 @@ export class GrayscaleSpr extends MySprite { ...@@ -441,37 +320,26 @@ export class GrayscaleSpr extends MySprite {
this.ctx.putImageData(c, rect.x, rect.y, 0, 0, rect.width, rect.height); this.ctx.putImageData(c, rect.x, rect.y, 0, 0, rect.width, rect.height);
} }
drawSelf() { drawSelf() {
super.drawSelf(); super.drawSelf();
this.createGSCanvas(); this.createGSCanvas();
} }
} }
export class BitMapLabel extends MySprite { export class BitMapLabel extends MySprite {
labelArr; labelArr;
baseUrl; baseUrl;
setText(data, text) { setText(data, text) {
this.labelArr = []; this.labelArr = [];
const labelArr = []; const labelArr = [];
const tmpArr = text.split(''); const tmpArr = text.split("");
let totalW = 0; let totalW = 0;
let h = 0; let h = 0;
for (const tmp of tmpArr) { for (let i = 0; i < tmpArr.length; i++) {
const label = new MySprite(this.ctx); const label = new MySprite(this.ctx);
label.init(data[tmp], 0); label.init(data[tmpArr[i]], 0);
this.addChild(label); this.addChild(label);
labelArr.push(label); labelArr.push(label);
...@@ -479,60 +347,52 @@ export class BitMapLabel extends MySprite { ...@@ -479,60 +347,52 @@ export class BitMapLabel extends MySprite {
h = label.height; h = label.height;
} }
this.width = totalW; this.width = totalW;
this.height = h; this.height = h;
let offX = -totalW / 2; let offX = -totalW / 2;
for (const label of labelArr) { for (let i = 0; i < labelArr.length; i++) {
label.x = offX; labelArr[i].x = offX;
offX += label.width; offX += labelArr[i].width;
} }
this.labelArr = labelArr; this.labelArr = labelArr;
} }
} }
export class Label extends MySprite { export class Label extends MySprite {
text: String;
text: string;
// fontSize:String = '40px'; // fontSize:String = '40px';
fontName = 'Verdana'; fontName: String = "Verdana";
textAlign = 'left'; textAlign: String = "left";
fontSize = 40; fontSize = 40;
fontColor = '#000000'; fontColor = "#000000";
fontWeight = 900; fontWeight = 900;
_maxWidth; maxWidth;
outline = 0; outline = 0;
outlineColor = '#ffffff'; outlineColor = "#ffffff";
maxSingalLineWidth = 0;
// _shadowFlag = false; _shadowFlag = false;
// _shadowColor; _shadowColor;
// _shadowOffsetX; _shadowOffsetX;
// _shadowOffsetY; _shadowOffsetY;
// _shadowBlur; _shadowBlur;
_outlineFlag = false; _outlineFlag = false;
_outLineWidth; _outLineWidth;
_outLineColor; _outLineColor;
_warpLineY = 0;
constructor(ctx = null) { constructor(ctx = null) {
super(ctx); super(ctx);
this.init(); this.init();
} }
refreshSize() { refreshSize() {
this.ctx.save(); this.ctx.save();
this.ctx.font = `${this.fontSize}px ${this.fontName}`; this.ctx.font = `${this.fontSize}px ${this.fontName}`;
this.ctx.textAlign = this.textAlign; this.ctx.textAlign = this.textAlign;
this.ctx.textBaseline = 'middle'; this.ctx.textBaseline = "middle";
this.ctx.fontWeight = this.fontWeight; this.ctx.fontWeight = this.fontWeight;
this._width = this.ctx.measureText(this.text).width; this._width = this.ctx.measureText(this.text).width;
...@@ -540,12 +400,10 @@ export class Label extends MySprite { ...@@ -540,12 +400,10 @@ export class Label extends MySprite {
this.refreshAnchorOff(); this.refreshAnchorOff();
this.ctx.restore(); this.ctx.restore();
} }
setMaxSize(w) { setMaxSize(w) {
this.maxWidth = w;
this._maxWidth = w;
this.refreshSize(); this.refreshSize();
if (this.width >= w) { if (this.width >= w) {
this.scaleX *= w / this.width; this.scaleX *= w / this.width;
...@@ -554,7 +412,6 @@ export class Label extends MySprite { ...@@ -554,7 +412,6 @@ export class Label extends MySprite {
} }
show(callBack = null) { show(callBack = null) {
this.visible = true; this.visible = true;
if (this.alpha >= 1) { if (this.alpha >= 1) {
...@@ -564,7 +421,7 @@ export class Label extends MySprite { ...@@ -564,7 +421,7 @@ export class Label extends MySprite {
const tween = new TWEEN.Tween(this) const tween = new TWEEN.Tween(this)
.to({ alpha: 1 }, 800) .to({ alpha: 1 }, 800)
// .easing(TWEEN.Easing.Quadratic.Out) // Use an easing function to make the animation smooth. // .easing(TWEEN.Easing.Quadratic.Out) // Use an easing function to make the animation smooth.
.onComplete(() => { .onComplete(function() {
if (callBack) { if (callBack) {
callBack(); callBack();
} }
...@@ -572,47 +429,41 @@ export class Label extends MySprite { ...@@ -572,47 +429,41 @@ export class Label extends MySprite {
.start(); // Start the tween immediately. .start(); // Start the tween immediately.
} }
// setShadow(offX = 0, offY = 2, blur = 2, color = 'rgba(0, 0, 0, 0.2)') { setShadow(offX = 2, offY = 2, blur = 2, color = "rgba(0, 0, 0, 0.2)") {
// this._shadowFlag = true;
// this._shadowFlag = true; this._shadowColor = color;
// this._shadowColor = color; // 将阴影向右移动15px,向上移动10px
// // 将阴影向右移动15px,向上移动10px this._shadowOffsetX = offX;
// this._shadowOffsetX = 5; this._shadowOffsetY = offY;
// this._shadowOffsetY = 5; // 轻微模糊阴影
// // 轻微模糊阴影 this._shadowBlur = blur;
// this._shadowBlur = 5; }
// }
setOutline(width = 5, color = '#ffffff') {
setOutline(width = 5, color = "#ffffff") {
this._outlineFlag = true; this._outlineFlag = true;
this._outLineWidth = width; this._outLineWidth = width;
this._outLineColor = color; this._outLineColor = color;
} }
drawText() { drawText() {
// console.log('in drawText', this.text); // console.log('in drawText', this.text);
if (!this.text) { return; } if (!this.text) {
return;
}
// if (this._shadowFlag) {
//
// this.ctx.shadowColor = this._shadowColor;
// // 将阴影向右移动15px,向上移动10px
// this.ctx.shadowOffsetX = this._shadowOffsetX;
// this.ctx.shadowOffsetY = this._shadowOffsetY;
// // 轻微模糊阴影
// this.ctx.shadowBlur = this._shadowBlur;
// }
if (this._shadowFlag) {
this.ctx.shadowColor = this._shadowColor;
// 将阴影向右移动15px,向上移动10px
this.ctx.shadowOffsetX = this._shadowOffsetX;
this.ctx.shadowOffsetY = this._shadowOffsetY;
// 轻微模糊阴影
this.ctx.shadowBlur = this._shadowBlur;
}
this.ctx.font = `${this.fontSize}px ${this.fontName}`; this.ctx.font = `${this.fontSize}px ${this.fontName}`;
this.ctx.textAlign = this.textAlign; this.ctx.textAlign = this.textAlign;
this.ctx.textBaseline = 'middle'; this.ctx.textBaseline = "middle";
this.ctx.fontWeight = this.fontWeight; this.ctx.fontWeight = this.fontWeight;
if (this._outlineFlag) { if (this._outlineFlag) {
...@@ -623,61 +474,152 @@ export class Label extends MySprite { ...@@ -623,61 +474,152 @@ export class Label extends MySprite {
this.ctx.fillStyle = this.fontColor; this.ctx.fillStyle = this.fontColor;
if (this.outline > 0) { if (this.outline > 0) {
this.ctx.lineWidth = this.outline; this.ctx.lineWidth = this.outline;
this.ctx.strokeStyle = this.outlineColor; this.ctx.strokeStyle = this.outlineColor;
this.ctx.strokeText(this.text, 0, 0); this.ctx.strokeText(this.text, 0, 0);
} }
// 当maxSingalLineWidth不为0时,对数据进行换行处理
if(this.maxSingalLineWidth !== 0 ){
var words = this.text.split(' ');
var line = '';
this._warpLineY = 0;
for(var n = 0; n < words.length; n++) {
var testLine = line + words[n] + ' ';
var metrics = this.ctx.measureText(testLine);
var testWidth = metrics.width;
if (testWidth > this.maxSingalLineWidth && n > 0) {
this.ctx.fillText(line, 0, this._warpLineY);
line = words[n] + ' ';
this._warpLineY += this.fontSize;
}
else {
line = testLine;
}
}
this.y = -this._warpLineY /2
this.ctx.fillText(line, 0, this._warpLineY);
}else{
this.ctx.fillText(this.text, 0, 0); this.ctx.fillText(this.text, 0, 0);
}
}
drawSelf() {
super.drawSelf();
this.drawText();
}
}
export class ShapeRectNew extends MySprite {
radius = 0;
fillColor = '#ffffff';
strokeColor = '#000000';
fill = true;
stroke = false;
lineWidth = 1;
setSize(w, h, r) {
this.width = w;
this.height = h;
this.radius = r;
}
setOutLine(color, lineWidth) {
this.stroke = true;
this.strokeColor = color;
this.lineWidth = lineWidth;
}
drawShape() {
const ctx = this.ctx;
const width = this.width;
const height = this.height;
const radius = this.radius;
ctx.save();
ctx.beginPath(0);
// 从右下角顺时针绘制,弧度从0到1/2PI
ctx.arc(width - radius, height - radius, radius, 0, Math.PI / 2);
// 矩形下边线
ctx.lineTo(radius, height);
// 左下角圆弧,弧度从1/2PI到PI
ctx.arc(radius, height - radius, radius, Math.PI / 2, Math.PI);
// 矩形左边线
ctx.lineTo(0, radius);
// 左上角圆弧,弧度从PI到3/2PI
ctx.arc(radius, radius, radius, Math.PI, Math.PI * 3 / 2);
// 上边线
ctx.lineTo(width - radius, 0);
// 右上角圆弧
ctx.arc(width - radius, radius, radius, Math.PI * 3 / 2, Math.PI * 2);
// 右边线
ctx.lineTo(width, height - radius);
ctx.closePath();
if (this.fill) {
ctx.fillStyle = this.fillColor;
ctx.fill();
}
if (this.stroke) {
ctx.lineWidth = this.lineWidth;
ctx.strokeStyle = this.strokeColor;
ctx.stroke();
}
ctx.restore();
} }
drawSelf() { drawSelf() {
super.drawSelf(); super.drawSelf();
this.drawText(); this.drawShape();
} }
} }
export class RichTextOld extends Label { export class RichTextOld extends Label {
textArr = []; textArr = [];
fontSize = 40; fontSize = 40;
setText(text: string, words) { setText(text: string, words) {
let newText = text; let newText = text;
for (const word of words) { for (let i = 0; i < words.length; i++) {
const word = words[i];
const re = new RegExp(word, 'g'); const re = new RegExp(word, "g");
newText = newText.replace( re, `#${word}#`); newText = newText.replace(re, `#${word}#`);
// newText = newText.replace(word, `#${word}#`); // newText = newText.replace(word, `#${word}#`);
} }
this.textArr = newText.split('#'); this.textArr = newText.split("#");
this.text = newText; this.text = newText;
// this.setSize(); // this.setSize();
} }
refreshSize() { refreshSize() {
this.ctx.save(); this.ctx.save();
this.ctx.font = `${this.fontSize}px ${this.fontName}`; this.ctx.font = `${this.fontSize}px ${this.fontName}`;
this.ctx.textAlign = this.textAlign; this.ctx.textAlign = this.textAlign;
this.ctx.textBaseline = 'middle'; this.ctx.textBaseline = "middle";
this.ctx.fontWeight = this.fontWeight; this.ctx.fontWeight = this.fontWeight;
let curX = 0; let curX = 0;
for (const text of this.textArr) { for (let i = 0; i < this.textArr.length; i++) {
const w = this.ctx.measureText(text).width; const w = this.ctx.measureText(this.textArr[i]).width;
curX += w; curX += w;
} }
...@@ -686,12 +628,9 @@ export class RichTextOld extends Label { ...@@ -686,12 +628,9 @@ export class RichTextOld extends Label {
this.refreshAnchorOff(); this.refreshAnchorOff();
this.ctx.restore(); this.ctx.restore();
} }
show(callBack = null) { show(callBack = null) {
// console.log(' in show '); // console.log(' in show ');
this.visible = true; this.visible = true;
// this.alpha = 0; // this.alpha = 0;
...@@ -699,185 +638,119 @@ export class RichTextOld extends Label { ...@@ -699,185 +638,119 @@ export class RichTextOld extends Label {
const tween = new TWEEN.Tween(this) const tween = new TWEEN.Tween(this)
.to({ alpha: 1 }, 800) .to({ alpha: 1 }, 800)
// .easing(TWEEN.Easing.Quadratic.Out) // Use an easing function to make the animation smooth. // .easing(TWEEN.Easing.Quadratic.Out) // Use an easing function to make the animation smooth.
.onComplete(() => { .onComplete(function() {
if (callBack) { if (callBack) {
callBack(); callBack();
} }
}) })
.start(); // Start the tween immediately. .start(); // Start the tween immediately.
} }
drawText() { drawText() {
// console.log('in drawText', this.text); // console.log('in drawText', this.text);
if (!this.text) { return; } if (!this.text) {
return;
}
this.ctx.font = `${this.fontSize}px ${this.fontName}`; this.ctx.font = `${this.fontSize}px ${this.fontName}`;
this.ctx.textAlign = this.textAlign; this.ctx.textAlign = this.textAlign;
this.ctx.textBaseline = 'middle'; this.ctx.textBaseline = "middle";
this.ctx.fontWeight = 900; this.ctx.fontWeight = 900;
this.ctx.lineWidth = 5; this.ctx.lineWidth = 5;
this.ctx.strokeStyle = '#ffffff'; this.ctx.strokeStyle = "#ffffff";
// this.ctx.strokeText(this.text, 0, 0); // this.ctx.strokeText(this.text, 0, 0);
this.ctx.fillStyle = '#000000'; this.ctx.fillStyle = "#000000";
// this.ctx.fillText(this.text, 0, 0); // this.ctx.fillText(this.text, 0, 0);
let curX = 0; let curX = 0;
for (let i = 0; i < this.textArr.length; i++) { for (let i = 0; i < this.textArr.length; i++) {
const w = this.ctx.measureText(this.textArr[i]).width; const w = this.ctx.measureText(this.textArr[i]).width;
if ((i + 1) % 2 === 0) { if ((i + 1) % 2 == 0) {
this.ctx.fillStyle = '#c8171e'; this.ctx.fillStyle = "#c8171e";
} else { } else {
this.ctx.fillStyle = '#000000'; this.ctx.fillStyle = "#000000";
} }
this.ctx.fillText(this.textArr[i], curX, 0); this.ctx.fillText(this.textArr[i], curX, 0);
curX += w; curX += w;
} }
} }
} }
export class RichText extends Label { export class RichText extends Label {
disH = 30; disH = 30;
constructor(ctx?: any) { constructor(ctx) {
super(ctx); super(ctx);
// this.dataArr = dataArr; // this.dataArr = dataArr;
} }
drawText() { drawText() {
if (!this.text) { if (!this.text) {
return; return;
} }
this.ctx.font = `${this.fontSize}px ${this.fontName}`; this.ctx.font = `${this.fontSize}px ${this.fontName}`;
this.ctx.textAlign = this.textAlign; this.ctx.textAlign = this.textAlign;
this.ctx.textBaseline = 'middle'; this.ctx.textBaseline = "middle";
this.ctx.fontWeight = this.fontWeight; this.ctx.fontWeight = this.fontWeight;
this.ctx.fillStyle = this.fontColor; this.ctx.fillStyle = this.fontColor;
const selfW = this.width * this.scaleX; const selfW = this.width * this.scaleX;
const chr = this.text.split(" ");
const chr = this.text.split(' '); let temp = "";
let temp = '';
const row = []; const row = [];
const w = selfW - 80; const w = selfW - 80;
const disH = (this.fontSize + this.disH) * this.scaleY; const disH = (this.fontSize + this.disH) * this.scaleY;
for (let a = 0; a < chr.length; a++) {
if (
this.ctx.measureText(temp).width < w &&
for (const c of chr) { this.ctx.measureText(temp + chr[a]).width <= w
if (this.ctx.measureText(temp).width < w && this.ctx.measureText(temp + (c)).width <= w) { ) {
temp += ' ' + c; temp += " " + chr[a];
} else { } else {
row.push(temp); row.push(temp);
temp = ' ' + c; temp = " " + chr[a];
} }
} }
row.push(temp); row.push(temp);
const x = 0; const x = 0;
const y = -row.length * disH / 2; const y = (-row.length * disH) / 2;
// for (let b = 0 ; b < row.length; b++) { // for (let b = 0 ; b < row.length; b++) {
// this.ctx.strokeText(row[b], x, y + ( b + 1 ) * disH ); // 每行字体y坐标间隔20 // this.ctx.strokeText(row[b], x, y + ( b + 1 ) * disH ); // 每行字体y坐标间隔20
// } // }
if (this._outlineFlag) { if (this._outlineFlag) {
this.ctx.lineWidth = this._outLineWidth; this.ctx.lineWidth = this._outLineWidth;
this.ctx.strokeStyle = this._outLineColor; this.ctx.strokeStyle = this._outLineColor;
for (let b = 0 ; b < row.length; b++) { for (let b = 0; b < row.length; b++) {
this.ctx.strokeText(row[b], x, y + ( b + 1 ) * disH ); // 每行字体y坐标间隔20 this.ctx.strokeText(row[b], x, y + (b + 1) * disH); // 每行字体y坐标间隔20
} }
// this.ctx.strokeText(this.text, 0, 0); // this.ctx.strokeText(this.text, 0, 0);
} }
// this.ctx.fillStyle = '#ff7600'; // this.ctx.fillStyle = '#ff7600';
for (let b = 0 ; b < row.length; b++) { for (let b = 0; b < row.length; b++) {
this.ctx.fillText(row[b], x, y + ( b + 1 ) * disH ); // 每行字体y坐标间隔20 this.ctx.fillText(row[b], x, y + (b + 1) * disH); // 每行字体y坐标间隔20
} }
} }
drawSelf() { drawSelf() {
super.drawSelf(); super.drawSelf();
this.drawText(); this.drawText();
} }
}
export class LineRect extends MySprite {
lineColor = '#ffffff';
lineWidth = 10;
setSize(w, h) {
this.width = w;
this.height = h;
}
drawLine() {
this.ctx.beginPath();
this.ctx.moveTo(this._offX, this._offY);
this.ctx.lineTo(this._offX + this.width, this._offY);
this.ctx.lineTo(this._offX + this.width, this._offY + this.height);
this.ctx.lineTo(this._offX, this._offY + this.height);
this.ctx.closePath();
this.ctx.lineWidth = this.lineWidth;
// this.ctx.fillStyle = "rgb(2,33,42)"; //指定填充颜色
// this.ctx.fill(); //对多边形进行填充
this.ctx.strokeStyle = this.lineColor; // "#ffffff";
this.ctx.stroke();
}
drawSelf() {
super.drawSelf();
this.drawLine();
}
} }
export class ShapeRect extends MySprite { export class ShapeRect extends MySprite {
fillColor = "#FF0000";
fillColor = '#FF0000';
setSize(w, h) { setSize(w, h) {
this.width = w; this.width = w;
...@@ -888,136 +761,64 @@ export class ShapeRect extends MySprite { ...@@ -888,136 +761,64 @@ export class ShapeRect extends MySprite {
} }
drawShape() { drawShape() {
this.ctx.fillStyle = this.fillColor; this.ctx.fillStyle = this.fillColor;
this.ctx.fillRect(this._offX, this._offY, this.width, this.height); this.ctx.fillRect(this._offX, this._offY, this.width, this.height);
} }
drawSelf() { drawSelf() {
super.drawSelf(); super.drawSelf();
this.drawShape(); this.drawShape();
} }
} }
export class ShapeCircle extends MySprite { export class ShapeCircle extends MySprite {
fillColor = "#FF0000";
fillColor = '#FF0000';
radius = 0; radius = 0;
startRadian = 0;
endRadian = 180;
strokeLineWidth = 5;
strokeColor = "#702dee";
drawType = "fill"
counterclockwise = false; // false 逆时针 true 顺时针
setRadius(r) { setRadius(r) {
this.anchorX = this.anchorY = 0.5; this.anchorX = this.anchorY = 0.5;
this.radius = r; this.radius = r;
this.width = r * 2; this.width = r * 2;
this.height = r * 2; this.height = r * 2;
} }
drawShape() { drawShape() {
switch(this.drawType){
case "stroke":
this.ctx.beginPath(); this.ctx.beginPath();
this.ctx.fillStyle = this.fillColor; this.ctx.strokeStyle = this.strokeColor;
this.ctx.arc(0, 0, this.radius, 0, angleToRadian(360)); this.ctx.lineWidth = this.strokeLineWidth
this.ctx.arc(0, 0, this.radius, this.startRadian, this.endRadian, this.counterclockwise);
this.ctx.stroke()
break;
default:
this.ctx.beginPath();
this.ctx.fillColor = this.fillColor;
this.ctx.arc(0, 0, this.radius, this.startRadian, this.endRadian);
this.ctx.fill(); this.ctx.fill();
break;
} }
drawSelf() {
super.drawSelf();
this.drawShape();
}
}
export class ShapeRectNew extends MySprite {
radius = 0;
fillColor = '#ffffff';
strokeColor = '#000000';
fill = true;
stroke = false;
lineWidth = 1;
setSize(w, h, r) {
this.width = w;
this.height = h;
this.radius = r;
}
setOutLine(color, lineWidth) {
this.stroke = true;
this.strokeColor = color;
this.lineWidth = lineWidth;
}
drawShape() {
const ctx = this.ctx;
const width = this.width;
const height = this.height;
const radius = this.radius;
ctx.save();
ctx.beginPath(0);
// 从右下角顺时针绘制,弧度从0到1/2PI
ctx.arc(width - radius, height - radius, radius, 0, Math.PI / 2);
// 矩形下边线
ctx.lineTo(radius, height);
// 左下角圆弧,弧度从1/2PI到PI
ctx.arc(radius, height - radius, radius, Math.PI / 2, Math.PI);
// 矩形左边线
ctx.lineTo(0, radius);
// 左上角圆弧,弧度从PI到3/2PI
ctx.arc(radius, radius, radius, Math.PI, Math.PI * 3 / 2);
// 上边线
ctx.lineTo(width - radius, 0);
// 右上角圆弧
ctx.arc(width - radius, radius, radius, Math.PI * 3 / 2, Math.PI * 2);
// 右边线
ctx.lineTo(width, height - radius);
ctx.closePath();
if (this.fill) {
ctx.fillStyle = this.fillColor;
ctx.fill();
}
if (this.stroke) {
ctx.lineWidth = this.lineWidth;
ctx.strokeStyle = this.strokeColor;
ctx.stroke();
}
ctx.restore();
} }
drawSelf() { drawSelf() {
super.drawSelf(); super.drawSelf();
this.drawShape(); this.drawShape();
} }
} }
export class MyAnimation extends MySprite {
export class MyAnimation extends MySprite {
frameArr = []; frameArr = [];
frameIndex = 0; frameIndex = 0;
playFlag = false; playFlag = false;
lastDateTime; lastDateTime;
curDelay = 0; curDelay = 0;
loop = false; loop = false;
playEndFunc; playEndFunc;
delayPerUnit = 1; delayPerUnit = 1;
...@@ -1026,7 +827,6 @@ export class MyAnimation extends MySprite { ...@@ -1026,7 +827,6 @@ export class MyAnimation extends MySprite {
reverseFlag = false; reverseFlag = false;
addFrameByImg(img) { addFrameByImg(img) {
const spr = new MySprite(this.ctx); const spr = new MySprite(this.ctx);
spr.init(img); spr.init(img);
this._refreshSize(img); this._refreshSize(img);
...@@ -1040,10 +840,8 @@ export class MyAnimation extends MySprite { ...@@ -1040,10 +840,8 @@ export class MyAnimation extends MySprite {
} }
addFrameByUrl(url) { addFrameByUrl(url) {
const spr = new MySprite(this.ctx); const spr = new MySprite(this.ctx);
spr.load(url).then(img => { spr.load(url).then(img => {
this._refreshSize(img); this._refreshSize(img);
}); });
spr.visible = false; spr.visible = false;
...@@ -1054,18 +852,15 @@ export class MyAnimation extends MySprite { ...@@ -1054,18 +852,15 @@ export class MyAnimation extends MySprite {
this.frameArr[this.frameIndex].visible = true; this.frameArr[this.frameIndex].visible = true;
} }
_refreshSize(img: any) { _refreshSize(img) {
if (this.width < img["width"]) {
if (this.width < img.width) { this.width = img["width"];
this.width = img.width;
} }
if (this.height < img.height) { if (this.height < img["height"]) {
this.height = img.height; this.height = img["height"];
} }
} }
play() { play() {
this.playFlag = true; this.playFlag = true;
this.lastDateTime = new Date().getTime(); this.lastDateTime = new Date().getTime();
...@@ -1075,13 +870,11 @@ export class MyAnimation extends MySprite { ...@@ -1075,13 +870,11 @@ export class MyAnimation extends MySprite {
this.playFlag = false; this.playFlag = false;
} }
replay() { replay() {
this.restartFlag = true; this.restartFlag = true;
this.play(); this.play();
} }
reverse() { reverse() {
this.reverseFlag = !this.reverseFlag; this.reverseFlag = !this.reverseFlag;
this.frameArr.reverse(); this.frameArr.reverse();
...@@ -1089,20 +882,18 @@ export class MyAnimation extends MySprite { ...@@ -1089,20 +882,18 @@ export class MyAnimation extends MySprite {
} }
showAllFrame() { showAllFrame() {
for (const frame of this.frameArr ) { for (let i = 0; i < this.frameArr.length; i++) {
frame.alpha = 1; this.frameArr[i].alpha = 1;
} }
} }
hideAllFrame() { hideAllFrame() {
for (const frame of this.frameArr) { for (let i = 0; i < this.frameArr.length; i++) {
frame.alpha = 0; this.frameArr[i].alpha = 0;
} }
} }
playEnd() { playEnd() {
this.playFlag = false; this.playFlag = false;
this.curDelay = 0; this.curDelay = 0;
...@@ -1119,7 +910,7 @@ export class MyAnimation extends MySprite { ...@@ -1119,7 +910,7 @@ export class MyAnimation extends MySprite {
this.frameArr[this.frameIndex].visible = false; this.frameArr[this.frameIndex].visible = false;
} }
this.frameIndex ++; this.frameIndex++;
if (this.frameIndex >= this.frameArr.length) { if (this.frameIndex >= this.frameArr.length) {
if (this.loop) { if (this.loop) {
this.frameIndex = 0; this.frameIndex = 0;
...@@ -1127,21 +918,16 @@ export class MyAnimation extends MySprite { ...@@ -1127,21 +918,16 @@ export class MyAnimation extends MySprite {
this.restartFlag = false; this.restartFlag = false;
this.frameIndex = 0; this.frameIndex = 0;
} else { } else {
this.frameIndex -- ; this.frameIndex--;
this.playEnd(); this.playEnd();
return; return;
} }
} }
this.frameArr[this.frameIndex].visible = true; this.frameArr[this.frameIndex].visible = true;
} }
_updateDelay(delay) { _updateDelay(delay) {
this.curDelay += delay; this.curDelay += delay;
if (this.curDelay < this.delayPerUnit) { if (this.curDelay < this.delayPerUnit) {
return; return;
...@@ -1151,7 +937,9 @@ export class MyAnimation extends MySprite { ...@@ -1151,7 +937,9 @@ export class MyAnimation extends MySprite {
} }
_updateLastDate() { _updateLastDate() {
if (!this.playFlag) { return; } if (!this.playFlag) {
return;
}
let delay = 0; let delay = 0;
if (this.lastDateTime) { if (this.lastDateTime) {
...@@ -1165,18 +953,18 @@ export class MyAnimation extends MySprite { ...@@ -1165,18 +953,18 @@ export class MyAnimation extends MySprite {
super.update($event); super.update($event);
this._updateLastDate(); this._updateLastDate();
} }
} }
// --------=========== util func =============------------- // --------=========== util func =============-------------
export function tweenChange(
export function tweenChange(item, obj, time = 0.8, callBack = null, easing = null, update = null) { item,
obj,
time = 0.8,
callBack = null,
easing = null,
update = null
) {
const tween = new TWEEN.Tween(item).to(obj, time * 1000); const tween = new TWEEN.Tween(item).to(obj, time * 1000);
if (callBack) { if (callBack) {
...@@ -1188,7 +976,7 @@ export function tweenChange(item, obj, time = 0.8, callBack = null, easing = nul ...@@ -1188,7 +976,7 @@ export function tweenChange(item, obj, time = 0.8, callBack = null, easing = nul
tween.easing(easing); tween.easing(easing);
} }
if (update) { if (update) {
tween.onUpdate( (a, b) => { tween.onUpdate((a, b) => {
update(a, b); update(a, b);
}); });
} }
...@@ -1197,11 +985,13 @@ export function tweenChange(item, obj, time = 0.8, callBack = null, easing = nul ...@@ -1197,11 +985,13 @@ export function tweenChange(item, obj, time = 0.8, callBack = null, easing = nul
return tween; return tween;
} }
export function rotateItem(
item,
rotation,
export function rotateItem(item, rotation, time = 0.8, callBack = null, easing = null) { time = 0.8,
callBack = null,
easing = null
) {
const tween = new TWEEN.Tween(item).to({ rotation }, time * 1000); const tween = new TWEEN.Tween(item).to({ rotation }, time * 1000);
if (callBack) { if (callBack) {
...@@ -1216,11 +1006,17 @@ export function rotateItem(item, rotation, time = 0.8, callBack = null, easing = ...@@ -1216,11 +1006,17 @@ export function rotateItem(item, rotation, time = 0.8, callBack = null, easing =
tween.start(); tween.start();
} }
export function scaleItem(
item,
export function scaleItem(item, scale, time = 0.8, callBack = null, easing = null) { scale,
time = 0.8,
const tween = new TWEEN.Tween(item).to({ scaleX: scale, scaleY: scale}, time * 1000); callBack = null,
easing = null
) {
const tween = new TWEEN.Tween(item).to(
{ scaleX: scale, scaleY: scale },
time * 1000
);
if (callBack) { if (callBack) {
tween.onComplete(() => { tween.onComplete(() => {
...@@ -1235,10 +1031,15 @@ export function scaleItem(item, scale, time = 0.8, callBack = null, easing = nul ...@@ -1235,10 +1031,15 @@ export function scaleItem(item, scale, time = 0.8, callBack = null, easing = nul
return tween; return tween;
} }
export function moveItem(
export function moveItem(item, x, y, time = 0.8, callBack = null, easing = null) { item,
x,
const tween = new TWEEN.Tween(item).to({ x, y}, time * 1000); y,
time = 0.8,
callBack = null,
easing = null
) {
const tween = new TWEEN.Tween(item).to({ x, y }, time * 1000);
if (callBack) { if (callBack) {
tween.onComplete(() => { tween.onComplete(() => {
...@@ -1254,36 +1055,26 @@ export function moveItem(item, x, y, time = 0.8, callBack = null, easing = null) ...@@ -1254,36 +1055,26 @@ export function moveItem(item, x, y, time = 0.8, callBack = null, easing = null)
return tween; return tween;
} }
export function endShow(item, s = 1) { export function endShow(item, s = 1) {
item.scaleX = item.scaleY = 0; item.scaleX = item.scaleY = 0;
item.alpha = 0; item.alpha = 0;
const tween = new TWEEN.Tween(item) const tween = new TWEEN.Tween(item)
.to({ alpha: 1, scaleX: s, scaleY: s }, 800) .to({ alpha: 1, scaleX: s, scaleY: s }, 800)
.easing(TWEEN.Easing.Elastic.Out) // Use an easing function to make the animation smooth. .easing(TWEEN.Easing.Elastic.Out) // Use an easing function to make the animation smooth.
.onComplete(() => { .onComplete(function() {})
})
.start(); .start();
} }
export function hideItem(item, time = 0.8, callBack = null, easing = null) { export function hideItem(item, time = 0.8, callBack = null, easing = null) {
if (item.alpha == 0) {
if (item.alpha === 0) {
return; return;
} }
const tween = new TWEEN.Tween(item) const tween = new TWEEN.Tween(item)
.to({alpha: 0}, time * 1000) .to({ alpha: 0 }, time * 1000)
// .easing(TWEEN.Easing.Quadratic.Out) // Use an easing function to make the animation smooth. // .easing(TWEEN.Easing.Quadratic.Out) // Use an easing function to make the animation smooth.
.onComplete(() => { .onComplete(function() {
if (callBack) { if (callBack) {
callBack(); callBack();
} }
...@@ -1296,10 +1087,8 @@ export function hideItem(item, time = 0.8, callBack = null, easing = null) { ...@@ -1296,10 +1087,8 @@ export function hideItem(item, time = 0.8, callBack = null, easing = null) {
tween.start(); tween.start();
} }
export function showItem(item, time = 0.8, callBack = null, easing = null) { export function showItem(item, time = 0.8, callBack = null, easing = null) {
if (item.alpha == 1) {
if (item.alpha === 1) {
if (callBack) { if (callBack) {
callBack(); callBack();
} }
...@@ -1308,9 +1097,9 @@ export function showItem(item, time = 0.8, callBack = null, easing = null) { ...@@ -1308,9 +1097,9 @@ export function showItem(item, time = 0.8, callBack = null, easing = null) {
item.visible = true; item.visible = true;
const tween = new TWEEN.Tween(item) const tween = new TWEEN.Tween(item)
.to({alpha: 1}, time * 1000) .to({ alpha: 1 }, time * 1000)
// .easing(TWEEN.Easing.Quadratic.Out) // Use an easing function to make the animation smooth. // .easing(TWEEN.Easing.Quadratic.Out) // Use an easing function to make the animation smooth.
.onComplete(() => { .onComplete(function() {
if (callBack) { if (callBack) {
callBack(); callBack();
} }
...@@ -1323,14 +1112,17 @@ export function showItem(item, time = 0.8, callBack = null, easing = null) { ...@@ -1323,14 +1112,17 @@ export function showItem(item, time = 0.8, callBack = null, easing = null) {
tween.start(); tween.start();
} }
export function alphaItem(
export function alphaItem(item, alpha, time = 0.8, callBack = null, easing = null) { item,
alpha,
time = 0.8,
callBack = null,
easing = null
) {
const tween = new TWEEN.Tween(item) const tween = new TWEEN.Tween(item)
.to({alpha}, time * 1000) .to({ alpha: alpha }, time * 1000)
// .easing(TWEEN.Easing.Quadratic.Out) // Use an easing function to make the animation smooth. // .easing(TWEEN.Easing.Quadratic.Out) // Use an easing function to make the animation smooth.
.onComplete(() => { .onComplete(function() {
if (callBack) { if (callBack) {
callBack(); callBack();
} }
...@@ -1343,14 +1135,11 @@ export function alphaItem(item, alpha, time = 0.8, callBack = null, easing = nul ...@@ -1343,14 +1135,11 @@ export function alphaItem(item, alpha, time = 0.8, callBack = null, easing = nul
tween.start(); tween.start();
} }
export function showStar(item, time = 0.8, callBack = null, easing = null) { export function showStar(item, time = 0.8, callBack = null, easing = null) {
const tween = new TWEEN.Tween(item) const tween = new TWEEN.Tween(item)
.to({alpha: 1, scale: 1}, time * 1000) .to({ alpha: 1, scale: 1 }, time * 1000)
// .easing(TWEEN.Easing.Quadratic.Out) // Use an easing function to make the animation smooth. // .easing(TWEEN.Easing.Quadratic.Out) // Use an easing function to make the animation smooth.
.onComplete(() => { .onComplete(function() {
if (callBack) { if (callBack) {
callBack(); callBack();
} }
...@@ -1363,97 +1152,103 @@ export function showStar(item, time = 0.8, callBack = null, easing = null) { ...@@ -1363,97 +1152,103 @@ export function showStar(item, time = 0.8, callBack = null, easing = null) {
tween.start(); tween.start();
} }
export function randomSortByArr(arr) { export function randomSortByArr(arr) {
const newArr = []; const newArr = [];
const tmpArr = arr.concat(); const tmpArr = arr.concat();
while (tmpArr.length > 0) { while (tmpArr.length > 0) {
const randomIndex = Math.floor( tmpArr.length * Math.random() ); const randomIndex = Math.floor(tmpArr.length * Math.random());
newArr.push(tmpArr[randomIndex]); newArr.push(tmpArr[randomIndex]);
tmpArr.splice(randomIndex, 1); tmpArr.splice(randomIndex, 1);
} }
return newArr; return newArr;
} }
export function radianToAngle(radian) { export function radianToAngle(radian) {
return radian * 180 / Math.PI; return (radian * 180) / Math.PI;
// 角度 = 弧度 * 180 / Math.PI; // 角度 = 弧度 * 180 / Math.PI;
} }
export function angleToRadian(angle) { export function angleToRadian(angle) {
return angle * Math.PI / 180; return (angle * Math.PI) / 180;
// 弧度= 角度 * Math.PI / 180; // 弧度= 角度 * Math.PI / 180;
} }
export function getPosByAngle(angle, len) { export function getPosByAngle(angle, len) {
const radian = (angle * Math.PI) / 180;
const radian = angle * Math.PI / 180;
const x = Math.sin(radian) * len; const x = Math.sin(radian) * len;
const y = Math.cos(radian) * len; const y = Math.cos(radian) * len;
return {x, y}; return { x, y };
} }
export function getAngleByPos(px, py, mx, my) { export function getAngleByPos(px, py, mx, my) {
const x = Math.abs(px - mx); const x = Math.abs(px - mx);
const y = Math.abs(py - my); const y = Math.abs(py - my);
const z = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)); const z = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
const cos = y / z; const cos = y / z;
const radina = Math.acos(cos); // 用反三角函数求弧度 const radina = Math.acos(cos); // 用反三角函数求弧度
let angle = Math.floor(180 / (Math.PI / radina) * 100) / 100; // 将弧度转换成角度 let angle = Math.floor((180 / (Math.PI / radina)) * 100) / 100; // 将弧度转换成角度
if (mx > px && my > py) {// 鼠标在第四象限 if (mx > px && my > py) {
// 鼠标在第四象限
angle = 180 - angle; angle = 180 - angle;
} }
if (mx === px && my > py) {// 鼠标在y轴负方向上 if (mx === px && my > py) {
// 鼠标在y轴负方向上
angle = 180; angle = 180;
} }
if (mx > px && my === py) {// 鼠标在x轴正方向上 if (mx > px && my === py) {
// 鼠标在x轴正方向上
angle = 90; angle = 90;
} }
if (mx < px && my > py) {// 鼠标在第三象限 if (mx < px && my > py) {
// 鼠标在第三象限
angle = 180 + angle; angle = 180 + angle;
} }
if (mx < px && my === py) {// 鼠标在x轴负方向 if (mx < px && my === py) {
// 鼠标在x轴负方向
angle = 270; angle = 270;
} }
if (mx < px && my < py) {// 鼠标在第二象限 if (mx < px && my < py) {
// 鼠标在第二象限
angle = 360 - angle; angle = 360 - angle;
} }
// console.log('angle: ', angle); // console.log('angle: ', angle);
return angle; return angle;
} }
export function removeItemFromArr(arr, item) { export function removeItemFromArr(arr, item) {
const index = arr.indexOf(item); const index = arr.indexOf(item);
if (index !== -1) { if (index != -1) {
arr.splice(index, 1); arr.splice(index, 1);
} }
} }
export function circleMove(
item,
x0,
y0,
time = 2,
export function circleMove(item, x0, y0, time = 2, addR = 360, xPer = 1, yPer = 1, callBack = null, easing = null) { addR = 360,
xPer = 1,
yPer = 1,
callBack = null,
easing = null
) {
const r = getPosDistance(item.x, item.y, x0, y0); const r = getPosDistance(item.x, item.y, x0, y0);
let a = getAngleByPos(item.x, item.y, x0, y0); let a = getAngleByPos(item.x, item.y, x0, y0);
a += 90; a += 90;
const obj = {r, a}; const obj = { r, a };
item._circleAngle = a; item._circleAngle = a;
const targetA = a + addR; const targetA = a + addR;
const tween = new TWEEN.Tween(item).to({_circleAngle: targetA}, time * 1000); const tween = new TWEEN.Tween(item).to(
{ _circleAngle: targetA },
time * 1000
);
if (callBack) { if (callBack) {
tween.onComplete(() => { tween.onComplete(() => {
...@@ -1464,14 +1259,13 @@ export function circleMove(item, x0, y0, time = 2, addR = 360, xPer = 1, yPer = ...@@ -1464,14 +1259,13 @@ export function circleMove(item, x0, y0, time = 2, addR = 360, xPer = 1, yPer =
tween.easing(easing); tween.easing(easing);
} }
tween.onUpdate( (item, progress) => { tween.onUpdate((item, progress) => {
// console.log(item._circleAngle); // console.log(item._circleAngle);
const r = obj.r; const r = obj.r;
const a = item._circleAngle; const a = item._circleAngle;
const x = x0 + r * xPer * Math.cos(a * Math.PI / 180); const x = x0 + r * xPer * Math.cos((a * Math.PI) / 180);
const y = y0 + r * yPer * Math.sin(a * Math.PI / 180); const y = y0 + r * yPer * Math.sin((a * Math.PI) / 180);
item.x = x; item.x = x;
item.y = y; item.y = y;
...@@ -1482,12 +1276,10 @@ export function circleMove(item, x0, y0, time = 2, addR = 360, xPer = 1, yPer = ...@@ -1482,12 +1276,10 @@ export function circleMove(item, x0, y0, time = 2, addR = 360, xPer = 1, yPer =
tween.start(); tween.start();
} }
export function getPosDistance(sx, sy, ex, ey) { export function getPosDistance(sx, sy, ex, ey) {
const _x = ex - sx; const _x = ex - sx;
const _y = ey - sy; const _y = ey - sy;
const len = Math.sqrt( Math.pow(_x, 2) + Math.pow(_y, 2) ); const len = Math.sqrt(Math.pow(_x, 2) + Math.pow(_y, 2));
return len; return len;
} }
...@@ -1502,29 +1294,6 @@ export function delayCall(callback, second) { ...@@ -1502,29 +1294,6 @@ export function delayCall(callback, second) {
.start(); .start();
} }
export function formatTime(fmt, date) {
// "yyyy-MM-dd HH:mm:ss";
const o = {
'M+': date.getMonth() + 1, // 月份
'd+': date.getDate(), // 日
'h+': date.getHours(), // 小时
'm+': date.getMinutes(), // 分
's+': date.getSeconds(), // 秒
'q+': Math.floor((date.getMonth() + 3) / 3), // 季度
S: date.getMilliseconds() // 毫秒
};
if (/(y+)/.test(fmt)) { fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length)); }
for (const k in o) {
if (new RegExp('(' + k + ')').test(fmt)) { fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1)
? (o[k]) : (('00' + o[k]).substr(('' + o[k]).length)));
}
}
return fmt;
}
export function getMinScale(item, maxLen) { export function getMinScale(item, maxLen) {
const sx = maxLen / item.width; const sx = maxLen / item.width;
const sy = maxLen / item.height; const sy = maxLen / item.height;
...@@ -1532,11 +1301,7 @@ export function getMinScale(item, maxLen) { ...@@ -1532,11 +1301,7 @@ export function getMinScale(item, maxLen) {
return minS; return minS;
} }
export function jelly(item, time = 0.7) { export function jelly(item, time = 0.7) {
if (item.jellyTween) { if (item.jellyTween) {
TWEEN.remove(item.jellyTween); TWEEN.remove(item.jellyTween);
} }
...@@ -1552,10 +1317,16 @@ export function jelly(item, time = 0.7) { ...@@ -1552,10 +1317,16 @@ export function jelly(item, time = 0.7) {
return; return;
} }
const data = arr[index]; const data = arr[index];
const t = tweenChange(item, {scaleX: data[0], scaleY: data[1]}, data[2], () => { const t = tweenChange(
index ++; item,
{ scaleX: data[0], scaleY: data[1] },
data[2],
() => {
index++;
run(); run();
}, TWEEN.Easing.Sinusoidal.InOut); },
TWEEN.Easing.Sinusoidal.InOut
);
item.jellyTween = t; item.jellyTween = t;
}; };
...@@ -1564,20 +1335,24 @@ export function jelly(item, time = 0.7) { ...@@ -1564,20 +1335,24 @@ export function jelly(item, time = 0.7) {
[baseSX * 0.98, baseSY * 1.02, t * 2], [baseSX * 0.98, baseSY * 1.02, t * 2],
[baseSX * 1.02, baseSY * 0.98, t * 2], [baseSX * 1.02, baseSY * 0.98, t * 2],
[baseSX * 0.99, baseSY * 1.01, t * 2], [baseSX * 0.99, baseSY * 1.01, t * 2],
[baseSX * 1.0, baseSY * 1.0, t * 2], [baseSX * 1.0, baseSY * 1.0, t * 2]
]; ];
run(); run();
} }
/**
* 烟花爆炸效果动画
export function showPopParticle(img, pos, parent, num = 20, minLen = 40, maxLen = 80, showTime = 0.4) { * @param img 颗粒的图片
* @param pos 爆点的坐标
* @param parent 必须传一个父类
for (let i = 0; i < num; i ++) { */
export function showPopParticle(img, pos, parent) {
const num = 20;
const maxLen = 100;
const minLen = 40;
for (let i = 0; i < num; i++) {
const particle = new MySprite(); const particle = new MySprite();
particle.init(img); particle.init(img);
particle.x = pos.x; particle.x = pos.x;
...@@ -1587,8 +1362,8 @@ export function showPopParticle(img, pos, parent, num = 20, minLen = 40, maxLen ...@@ -1587,8 +1362,8 @@ export function showPopParticle(img, pos, parent, num = 20, minLen = 40, maxLen
const randomR = 360 * Math.random(); const randomR = 360 * Math.random();
particle.rotation = randomR; particle.rotation = randomR;
const randomS = 0.3 + Math.random() * 0.7; const randomS = 0.5 + Math.random() * 0.5;
particle.setScaleXY(randomS * 0.3); particle.setScaleXY(randomS);
const randomX = Math.random() * 20 - 10; const randomX = Math.random() * 20 - 10;
particle.x += randomX; particle.x += randomX;
...@@ -1596,39 +1371,23 @@ export function showPopParticle(img, pos, parent, num = 20, minLen = 40, maxLen ...@@ -1596,39 +1371,23 @@ export function showPopParticle(img, pos, parent, num = 20, minLen = 40, maxLen
const randomY = Math.random() * 20 - 10; const randomY = Math.random() * 20 - 10;
particle.y += randomY; particle.y += randomY;
const randomL = minLen + Math.random() * (maxLen - minLen); const randomL = minLen + Math.random() * maxLen;
const randomA = 360 * Math.random(); const randomA = 360 * Math.random();
const randomT = getPosByAngle(randomA, randomL); const randomT = getPosByAngle(randomA, randomL);
moveItem(particle, particle.x + randomT.x, particle.y + randomT.y, showTime, () => { moveItem(
particle,
particle.x + randomT.x,
particle.y + randomT.y,
}, TWEEN.Easing.Exponential.Out); 0.4,
() => {},
// scaleItem(particle, 0, 0.6, () => { TWEEN.Easing.Exponential.Out
// );
// });
scaleItem(particle, randomS, 0.6, () => {
}, TWEEN.Easing.Exponential.Out);
setTimeout(() => {
hideItem(particle, 0.4, () => {
}, TWEEN.Easing.Cubic.In);
}, showTime * 0.5 * 1000);
scaleItem(particle, 0, 0.6, () => {});
} }
} }
export function shake(item, time = 0.5, callback = null, rate = 1) { export function shake(item, time = 0.5, callback = null, rate = 1) {
if (item.shakeTween) { if (item.shakeTween) {
return; return;
} }
...@@ -1640,37 +1399,62 @@ export function shake(item, time = 0.5, callback = null, rate = 1) { ...@@ -1640,37 +1399,62 @@ export function shake(item, time = 0.5, callback = null, rate = 1) {
const baseY = item.y; const baseY = item.y;
const easing = TWEEN.Easing.Sinusoidal.InOut; const easing = TWEEN.Easing.Sinusoidal.InOut;
const move4 = () => { const move4 = () => {
moveItem(item, baseX, baseY, time / 4, () => { moveItem(
item,
baseX,
baseY,
time / 4,
() => {
item.shakeTween = false; item.shakeTween = false;
if (callback) { if (callback) {
callback(); callback();
} }
}, easing); },
easing
);
}; };
const move3 = () => { const move3 = () => {
moveItem(item, baseX + offX / 4, baseY + offY / 4, time / 4, () => { moveItem(
item,
baseX + offX / 4,
baseY + offY / 4,
time / 4,
() => {
move4(); move4();
}, easing); },
easing
);
}; };
const move2 = () => { const move2 = () => {
moveItem(item, baseX - offX / 4 * 3, baseY - offY / 4 * 3, time / 4, () => { moveItem(
item,
baseX - (offX / 4) * 3,
baseY - (offY / 4) * 3,
time / 4,
() => {
move3(); move3();
}, easing); },
easing
);
}; };
const move1 = () => { const move1 = () => {
moveItem(item, baseX + offX, baseY + offY, time / 7.5, () => { moveItem(
item,
baseX + offX,
baseY + offY,
time / 8,
() => {
move2(); move2();
}, easing); },
easing
);
}; };
move1(); move1();
} }
// --------------- custom class -------------------- // --------------- custom class --------------------
.game-container {
width: 100%;
height: 100%;
background: #ffffff;
background-size: cover;
}
#canvas {
}
@font-face
{
font-family: 'BRLNSDB';
src: url("../../assets/font/BRLNSDB.TTF") ;
}
.game-container {
width: 100%;
height: 100%;
//background-image: url("/assets/listen-read-circle/bg.jpg");
background: #ffffff;
background-repeat: no-repeat;
background-size: cover;
}
.read-and-point-copy {
width: 500px;
height: 85px;
object-fit: contain;
text-shadow: 0 6px 4px rgba(0, 0, 0, 0.5), 0 3px 0 #fffecc;
font-family: Questrian;
font-size: 72px;
font-weight: normal;
font-stretch: normal;
font-style: normal;
line-height: normal;
letter-spacing: normal;
color: #f8e424;
}
.read-and-point-copy .text-style-1 {
font-size: 48px;
}
.read-and-point-copy .text-style-2 {
font-size: 64px;
}
@font-face
{
font-family: 'BRLNSDB';
src: url("../../assets/font/BRLNSDB.TTF") ;
}
@font-face
{
font-family: 'RoundedBold';
src: url("../../assets/font/ArialRoundedBold.otf") ;
}
@font-face{
font-family: 'BRLNSB_1';
src: url("../../assets/font/BerlinSansFB/BRLNSB_1.TTF") ;
}
@font-face{
font-family: 'BerlinSansFBDemi-Bold';
src: url("../../assets/font/BerlinSansFB/BRLNSDB_1.TTF") ;
}
@font-face{
font-family: 'BRLNSR_1';
src: url("../../assets/font/BerlinSansFB/BRLNSR_1.TTF") ;
}
@font-face{
font-family: 'GOTHIC_1';
src: url("../../assets/font/CenturyGothic/GOTHIC_1.TTF") ;
}
@font-face{
font-family: 'GOTHICB_1';
src: url("../../assets/font/CenturyGothic/GOTHICB_1.TTF") ;
}
@font-face{
font-family: 'GOTHICBI_1';
src: url("../../assets/font/CenturyGothic/GOTHICBI_1.TTF") ;
}
@font-face{
font-family: 'GOTHICI_1';
src: url("../../assets/font/CenturyGothic/GOTHICI_1.TTF") ;
}
@font-face{
font-family: 'MMTextBook';
src: url("../../assets/font/MMTextBook/MMTextBook.otf") ;
}
@font-face{
font-family: 'MMTextBook-Bold';
src: url("../../assets/font/MMTextBook/MMTextBook-Bold.otf") ;
}
@font-face{
font-family: 'MMTextBook-BoldItalic';
src: url("../../assets/font/MMTextBook/MMTextBook-BoldItalic.otf") ;
}
@font-face{
font-family: 'MMTextBook-Italic';
src: url("../../assets/font/MMTextBook/MMTextBook-Italic.otf") ;
}
import {Component, ElementRef, ViewChild, OnInit, Input, OnDestroy, HostListener} from '@angular/core'; import {
Component,
ElementRef,
ViewChild,
OnInit,
Input,
OnDestroy,
HostListener
} from "@angular/core";
import { import {
MySprite,
getMinScale,
ShapeRect,
ShapeCircle,
tweenChange,
randomSortByArr,
Label, Label,
MySprite, tweenChange, showPopParticle,
moveItem,
removeItemFromArr,
rotateItem,
hideItem,
showItem,
ShapeRectNew
} from "./Unit";
import { localImages, localAudios } from "./resources";
import { Cartoon } from './Cartoon'
import { Subject } from "rxjs";
import { debounceTime } from "rxjs/operators";
import * as _ from "lodash";
import TWEEN from "@tweenjs/tween.js";
const defauleFormData = {
dataArray:[
{
time: 15,
question:{
text:"This is Demo, please set question in form page",
image_url:"question-image",
audio_url:""
},
choice:{
correct:{ isText: true, text: "Correct choice", image_url: "bg-240-180" },
incorrect:[
{ isText: true, text: "Incorrect choice", image_url: "bg-240-180" },
{ isText: true, text: "Incorrect choice", image_url: "bg-240-180" },
{ isText: true, text: "Incorrect choice", image_url: "bg-240-180" },
]
}
},
{
time: 15,
question:{
text:"This is Demo, please set question in form page",
image_url:"",
audio_url:"sm-display"
},
choice:{
correct:{ isText: true, text: "Correct choice", image_url: "bg-240-180" },
incorrect:[
{ isText: true, text: "Incorrect choice", image_url: "bg-240-180" },
{ isText: true, text: "Incorrect choice", image_url: "bg-240-180" },
{ isText: true, text: "Incorrect choice", image_url: "bg-240-180" },
]
}
},
{
time: 15,
question:{
text:"",
image_url:"question-image",
audio_url:"sm-display"
},
choice:{
correct:{ isText: true, text: "Correct choice", image_url: "bg-240-180" },
incorrect:[
{ isText: true, text: "Incorrect choice", image_url: "bg-240-180" },
{ isText: true, text: "Incorrect choice", image_url: "bg-240-180" },
{ isText: true, text: "Incorrect choice", image_url: "bg-240-180" },
]
}
},
{
time: 15,
question:{
text:"This is Demo, please set question in form page",
image_url:"",
audio_url:""
},
choice:{
correct:{ isText: true, text: "Correct choice", image_url: "bg-240-180" },
incorrect:[
{ isText: true, text: "Incorrect choice", image_url: "bg-240-180" },
{ isText: true, text: "Incorrect choice", image_url: "bg-240-180" },
{ isText: true, text: "Incorrect choice", image_url: "bg-240-180" },
]
}
}
]
}
} from './Unit'; @Component({
import {res, resAudio} from './resources'; selector: "app-play",
templateUrl: "./play.component.html",
styleUrls: ["./play.component.scss"]
})
import {Subject} from 'rxjs'; export class PlayComponent implements OnInit, OnDestroy {
import {debounceTime} from 'rxjs/operators';
import TWEEN from '@tweenjs/tween.js'; g_cartoon = new Cartoon()
// ------------ 全局数据 ------------
g_stage; //中心舞台
g_enableTouch = true; // 触摸使能
g_canvasLeft;
g_canvasTop;
g_animationId: any;
g_mapScale = 1; // 缩放比例
g_KEY = "DataKey_MultipleChoice";
g_canvasWidth = 1280;
g_canvasHeight = 720;
g_canvasBaseW = 1280;
g_canvasBaseH = 720;
g_winResizeEventStream = new Subject();
g_clickX; // 点击坐标 X
g_clickY; // 点击坐标 Y
g_ctx; // canvas 实例
g_data; // 数据
g_formData; // 核心表单数据
g_teacherFlag = false; // 默认角色
g_currentUser;
// ------------------------------------
// ------------ 私有数据 ------------
m_clickQueue = {} //点击事件处理队列
m_endPageArr;
m_showPetalFlag;
m_elementPetalArr;
m_showElementPetalFlag;
m_PetalImage = "petal_" // 飘落动画
m_renderArr // 渲染队列
// ------------------------------------
// ------------ 游戏逻辑数据 ------------
// ------------------------------------
@Component({
selector: 'app-play',
templateUrl: './play.component.html',
styleUrls: ['./play.component.css']
})
export class PlayComponent implements OnInit, OnDestroy {
@ViewChild('canvas', {static: true }) canvas: ElementRef; // ------------ 消息 ------------
@ViewChild('wrap', {static: true }) wrap: ElementRef; // ------------------------------------
// 数据
data;
ctx; // ------------ 调试变量 ------------
g_EnableStageRuler = true; // 使能舞台背景格尺
g_ForceChangeDefaultRole = false // 强制当前角色为默认角色
g_EnableTestSendEvent = false // 发送模拟Web数据
// ------------------------------------
canvasWidth = 1280; // canvas实际宽度
canvasHeight = 720; // canvas实际高度
canvasBaseW = 1280; // canvas 资源预设宽度 // 当数据加载完毕后,执行
canvasBaseH = 720; // canvas 资源预设高度 systemReady(){
this.initGame()
}
mx; // 点击x坐标 // 屏幕尺寸变化后执行
my; // 点击y坐标 handleScreenResize(){
}
// 资源 restartGame() {
rawImages = new Map(res); this.initGame()
rawAudios = new Map(resAudio); }
images = new Map();
animationId: any; // ------------------------------------------------------------------------------
winResizeEventStream = new Subject(); // 游戏核心处理区
// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
//
//
//
// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
audioObj = {}; initGame(){
}
renderArr;
mapScale = 1;
canvasLeft;
canvasTop;
saveKey = 'test_0011';
btnLeft;
btnRight;
pic1;
pic2;
canTouch = true;
curPic;
@HostListener('window:resize', ['$event'])
onResize(event) {
this.winResizeEventStream.next();
}
ngOnInit() {
this.data = {};
// 获取数据
const getData = (<any> window).courseware.getData;
getData((data) => {
if (data && typeof data == 'object') {
this.data = data;
}
// console.log('data:' , data);
// 初始化 各事件监听
this.initListener();
// 若无数据 则为预览模式 需要填充一些默认数据用来显示
this.initDefaultData();
// 初始化 音频资源
this.initAudio();
// 初始化 图片资源
this.initImg();
// 开始预加载资源
this.load();
}, this.saveKey);
}
// --------------------------------------------------
// -------------- Template function ---------------
// --------------------------------------------------
// --------------------------------------------------
// --------------------------------------------------
//
// _________
// / /.
// .-------------. /_________/ |
// / / | | | |
// /+============+\ | | |====| | |
// ||C:\> || | | | |
// || || | | |====| | |
// || || | | ___ | |
// || || | | |166| | |
// || ||/@@@ | --- | |
// \+============+/ @ |_________|./.
// @ .. ....'
// ..................@ __.'.' ''
// /oooooooooooooooo// ///
// /................// /_/
// ------------------
//
// --------------------------------------------------
@ViewChild("canvas") canvas: ElementRef;
@ViewChild("wrap") wrap: ElementRef;
@HostListener("window:resize", ["$event"])
onResize(event) {
this.g_winResizeEventStream.next();
}
ngOnDestroy() { ngOnDestroy() {
window['curCtx'] = null; window["curCtx"] = null;
window.cancelAnimationFrame(this.animationId); window.cancelAnimationFrame(this.g_animationId);
}
ngOnInit() {
const getData = (<any>window).courseware.getData;
getData(data => {
if (window['air'].airClassInfo.user.classRole == 'tea') {
if(!this.g_ForceChangeDefaultRole){
this.g_teacherFlag = true;
}
}
this.g_currentUser = window['air'].airClassInfo.user;
if (data && typeof data == "object") {
this.g_data = data;
this.g_formData = data.contentObj;
} else {
this.g_data = {};
} }
if (!this.g_data.contentObj) {
this.g_data.contentObj = {};
this.g_formData = {};
}
load() { this.initDefaultData();
this.initAudio();
this.initImg();
// 预加载资源 // 预加载资源
this.loadResources().then(() => { this.g_cartoon.loadResources().then(() => {
window["air"].hideAirClassLoading(this.saveKey, this.data); window["air"].hideAirClassLoading(this.g_KEY, this.g_data);
this.init(); this.initSystem();
this.update(); this.update();
this.systemReady()
}); });
this.initListener();
}, this.g_KEY);
} }
// ----------------------------------
// 初始化默认数据
// ----------------------------------
initDefaultData() {
if ( Object.keys(this.g_formData).length===0 ) {
// console.log("Init default form data")
this.g_formData = defauleFormData;
}
}
init() {
this.initCtx(); // ----------------------------------
this.initData(); // 初始化音乐
this.initView(); // ----------------------------------
initAudio() {
const contentObj = this.g_formData;
if (!contentObj) {
return;
}
const arr = contentObj.dataArray;
// 添加用户上传音效
if (arr) {
for (let i = 0; i < arr.length; i++) {
if(arr[i].question.audio_url){
this.g_cartoon.addAudio(arr[i].question.audio_url, arr[i].question.audio_url);
}
}
}
// 添加本地音效
for( var key in localAudios ){
this.g_cartoon.addAudio( key, localAudios[key] );
}
} }
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; // ----------------------------------
initImg() {
const contentObj = this.g_formData;
if (contentObj) {
const addPicUrl = url => {
if (url) {
this.g_cartoon.addImage(url, url);
}
};
const arr = this.g_data.contentObj.dataArray;
if (arr) {
for (let i = 0; i < arr.length; i++) {
addPicUrl(arr[i].question.image_url);
addPicUrl(arr[i].choice.correct.image_url);
arr[i].choice.incorrect.forEach(item=>{
addPicUrl(item.image_url);
})
}
}
}
// 添加本地图片
for( var key in localImages ){
this.g_cartoon.addImage( key, localImages[key] );
}
}
window['curCtx'] = this.ctx; mapDown(event) {
if (!this.g_enableTouch) {
return;
}
for(let cartoonID in this.m_clickQueue){
if (this.checkClickTarget(this.g_cartoon.getCartoonElementRef(cartoonID))) {
this.g_enableTouch = false;
this.m_clickQueue[cartoonID]();
return;
}
}
} }
mapMove(event) {}
mapUp(event) {}
update() {
this.g_animationId = window.requestAnimationFrame(this.update.bind(this));
// 清除画布内容
this.g_ctx.clearRect(0, 0, this.g_canvasWidth, this.g_canvasHeight);
TWEEN.update();
this.updateArr(this.m_renderArr);
this.updateArr(this.m_endPageArr);
this.updateArr(this.m_elementPetalArr);
}
updateItem(item) { updateItem(item) {
...@@ -154,6 +383,7 @@ export class PlayComponent implements OnInit, OnDestroy { ...@@ -154,6 +383,7 @@ export class PlayComponent implements OnInit, OnDestroy {
} }
} }
updateArr(arr) { updateArr(arr) {
if (!arr) { if (!arr) {
return; return;
...@@ -164,209 +394,131 @@ export class PlayComponent implements OnInit, OnDestroy { ...@@ -164,209 +394,131 @@ export class PlayComponent implements OnInit, OnDestroy {
} }
initListener() {
this.g_winResizeEventStream.pipe(debounceTime(500)).subscribe(data => {
this.renderAfterResize();
});
if (this.IsPC()) {
this.canvas.nativeElement.addEventListener("mousedown", event => {
setMxMyByMouse(event);
this.mapDown(event);
});
this.canvas.nativeElement.addEventListener("mousemove", event => {
setMxMyByMouse(event);
this.mapMove(event);
});
this.canvas.nativeElement.addEventListener("mouseup", event => {
setMxMyByMouse(event);
this.mapUp(event);
});
const setMxMyByMouse = event => {
this.g_clickX = event.offsetX;
this.g_clickY = event.offsetY;
};
} else {
this.canvas.nativeElement.addEventListener("touchstart", event => {
setMxMyByTouch(event);
this.mapDown(event);
});
initListener() { this.canvas.nativeElement.addEventListener("touchmove", event => {
setMxMyByTouch(event);
this.mapMove(event);
});
this.winResizeEventStream this.canvas.nativeElement.addEventListener("touchend", event => {
.pipe(debounceTime(500)) setMxMyByTouch(event);
.subscribe(data => { this.mapUp(event);
this.renderAfterResize();
}); });
this.canvas.nativeElement.addEventListener("touchcancel", event => {
setMxMyByTouch(event);
this.mapUp(event);
});
// --------------------------------------------- const setMxMyByTouch = event => {
const setParentOffset = () => {
const rect = this.canvas.nativeElement.getBoundingClientRect();
this.canvasLeft = rect.left;
this.canvasTop = rect.top;
};
const setMxMyByTouch = (event) => {
if (event.touches.length <= 0) { if (event.touches.length <= 0) {
return; return;
} }
if (this.canvasLeft == null) {
if (this.g_canvasLeft == null) {
setParentOffset(); setParentOffset();
} }
this.mx = event.touches[0].pageX - this.canvasLeft;
this.my = event.touches[0].pageY - this.canvasTop;
};
const setMxMyByMouse = (event) => { this.g_clickX = event.touches[0].pageX - this.g_canvasLeft;
this.mx = event.offsetX; this.g_clickY = event.touches[0].pageY - this.g_canvasTop;
this.my = event.offsetY;
}; };
// ---------------------------------------------
let firstTouch = true; const setParentOffset = () => {
const rect = this.canvas.nativeElement.getBoundingClientRect();
const touchDownFunc = (e) => { this.g_canvasLeft = rect.left;
if (firstTouch) { this.g_canvasTop = rect.top;
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) { showArr(arr) {
if (!arr) {
const audio = this.audioObj[key]; return;
if (audio) {
if (now) {
audio.pause();
audio.currentTime = 0;
}
if (callback) {
audio.onended = () => {
callback();
};
} }
audio.play(); for (let i = 0; i < arr.length; i++) {
arr[i].visible = true;
} }
} }
hideArr(arr) {
if (!arr) {
return;
}
for (let i = 0; i < arr.length; i++) {
arr[i].visible = false;
}
}
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) IsPC() {
.then(() => { if (window["ELECTRON"]) {
// this.images.set(key, img); return false; // 封装客户端标记
})
.catch(err => console.log(err));
pr.push(a);
});
return Promise.all(pr);
} }
if (
preload(url) { document.body.ontouchmove !== undefined &&
return new Promise((resolve, reject) => { document.body.ontouchmove !== undefined
const img = new Image(); ) {
// img.crossOrigin = "anonymous"; return false;
img.onload = () => resolve(img); } else {
img.onerror = reject; return true;
img.src = url;
});
} }
preloadAudio(url) {
return new Promise((resolve, reject) => {
const audio = new Audio();
audio.oncanplay = (a) => {
resolve();
};
audio.onerror = () => {
reject();
};
audio.src = url;
audio.load();
});
} }
renderAfterResize() { renderAfterResize() {
this.canvasWidth = this.wrap.nativeElement.clientWidth; this.g_canvasWidth = this.wrap.nativeElement.clientWidth;
this.canvasHeight = this.wrap.nativeElement.clientHeight; this.g_canvasHeight = this.wrap.nativeElement.clientHeight;
this.init(); this.update();
this.handleScreenResize()
} }
checkClickTarget(target) { checkClickTarget(target) {
if (!target) {
return false;
}
const rect = target.getBoundingBox(); const rect = target.getBoundingBox();
if (this.checkPointInRect(this.g_clickX, this.g_clickY, rect)) {
if (this.checkPointInRect(this.mx, this.my, rect)) {
return true; return true;
} }
return false; return false;
} }
getWorlRect(target) {
getWorlRect(target) {
let rect = target.getBoundingBox(); let rect = target.getBoundingBox();
if (target.parent) { if (target.parent) {
const pRect = this.getWorlRect(target.parent); const pRect = this.getWorlRect(target.parent);
rect.x += pRect.x; rect.x += pRect.x;
rect.y += pRect.y; rect.y += pRect.y;
...@@ -374,6 +526,7 @@ export class PlayComponent implements OnInit, OnDestroy { ...@@ -374,6 +526,7 @@ export class PlayComponent implements OnInit, OnDestroy {
return rect; return rect;
} }
checkPointInRect(x, y, rect) { checkPointInRect(x, y, rect) {
if (x >= rect.x && x <= rect.x + rect.width) { if (x >= rect.x && x <= rect.x + rect.width) {
if (y >= rect.y && y <= rect.y + rect.height) { if (y >= rect.y && y <= rect.y + rect.height) {
...@@ -384,296 +537,348 @@ export class PlayComponent implements OnInit, OnDestroy { ...@@ -384,296 +537,348 @@ export class PlayComponent implements OnInit, OnDestroy {
} }
getPosByAngle(angle, len) {
const radian = (angle * Math.PI) / 180;
const x = Math.sin(radian) * len;
addUrlToAudioObj(key, url = null, vlomue = 1, loop = false, callback = null) { const y = Math.cos(radian) * len;
return { x, y };
const audioObj = this.audioObj;
if (url == null) {
url = key;
} }
this.rawAudios.set(key, url);
const audio = new Audio(); getPosDistance(sx, sy, ex, ey) {
audio.src = url; const _x = ex - sx;
audio.load(); const _y = ey - sy;
audio.loop = loop; const len = Math.sqrt(Math.pow(_x, 2) + Math.pow(_y, 2));
audio.volume = vlomue; return len;
audioObj[key] = audio;
if (callback) {
audio.onended = () => {
callback();
};
}
} }
addUrlToImages(url) {
this.rawImages.set(url, url); subscribeClickEvent(id,callback){
this.m_clickQueue[id] = callback
} }
// 全局初始化入口,当资源加载完毕后执行
initSystem() {
this.g_canvasWidth = this.wrap.nativeElement.clientWidth;
this.g_canvasHeight = this.wrap.nativeElement.clientHeight;
const sx = this.g_canvasWidth / this.g_canvasBaseW;
const sy = this.g_canvasHeight / this.g_canvasBaseH;
const s = Math.min(sx, sy);
this.g_mapScale = s;
this.g_cartoon.mapScale = this.g_mapScale;
this.g_cartoon.clientWidth = this.wrap.nativeElement.clientWidth;
this.g_cartoon.clientHeight = this.wrap.nativeElement.clientHeight;
// ======================================================编写区域========================================================================== this.m_renderArr = [];
this.g_enableTouch = true;
this.g_ctx = this.canvas.nativeElement.getContext("2d");
this.canvas.nativeElement.width = this.g_canvasWidth;
this.canvas.nativeElement.height = this.g_canvasHeight;
window["curCtx"] = this.g_ctx;
// 初始化舞台
this.initStage();
this.update();
}
initStage() {
const bgWidth = 1280;
const bgHeight = 720;
this.g_cartoon.stageWidth = bgWidth*this.g_mapScale;
this.g_cartoon.stageHeight= bgHeight*this.g_mapScale;
/** const imageColor = this.g_cartoon.createCartoonElement("background-color", "ShapeRect").ref;
* 添加默认数据 便于无数据时的展示 // console.log( "stageWidth: " + this.g_cartoon.stageWidth + " stageHeight" + this.g_cartoon.stageHeight)
*/ // console.log( "clientWidth: " + this.g_cartoon.clientWidth + " clientHeight" + this.g_cartoon.clientHeight)
initDefaultData() { // console.log( "canvasWidth: " + this.g_canvasWidth + " canvasHeight" + this.g_canvasHeight)
// console.log( "mapScale: " + this.g_mapScale )
if (!this.data.pic_url) { imageColor.x = this.g_cartoon.clientWidth / 2 ;
this.data.pic_url = 'assets/play/default/pic.jpg'; imageColor.y = this.g_cartoon.clientHeight / 2 ;
this.data.pic_url_2 = 'assets/play/default/pic.jpg'; imageColor.setSize(this.g_cartoon.clientWidth, this.g_cartoon.clientHeight);
imageColor.init();
if(this.g_EnableStageRuler){
imageColor.fillColor = '#BE3C38';
} }
} this.m_renderArr.push(imageColor);
/**
* 添加预加载图片
*/
initImg() {
this.addUrlToImages(this.data.pic_url);
this.addUrlToImages(this.data.pic_url_2);
const image = this.g_cartoon.createCartoonElement("bg_1280_720_Ruler", "MySprite").ref;
image.init(this.g_cartoon.images.get("bg_1280_720_Ruler"));
image.x = this.g_canvasWidth / 2;
image.y = this.g_canvasHeight /2;
image.visible = this.g_EnableStageRuler
this.g_cartoon.setOrigin( image.x-(bgWidth/2*this.g_mapScale), image.y-(bgHeight/2*this.g_mapScale) )
this.g_cartoon.setRelativeOrigin( -bgWidth/2, -bgHeight/2)
image.setScaleXY(this.g_mapScale);
this.m_renderArr.push(image);
this.g_stage = image
} }
/** render(ele){
* 添加预加载音频 this.m_renderArr.push(ele);
*/ }
initAudio() {
// 音频资源 sendServerEvent(key, data) {
this.addUrlToAudioObj(this.data.audio_url); const c = (<any> window).courseware;
this.addUrlToAudioObj(this.data.audio_url_2); c.sendEvent(key, JSON.stringify(data));
}
// 音效 addServerListener(msg_key, callback) {
this.addUrlToAudioObj('click', this.rawAudios.get('click'), 0.3); const c = (<any> window).courseware;
c.onEvent(msg_key, (data,next) => {
callback(JSON.parse(data))
next && next();
});
}
randomArray_shuffle(array) {
var input = array;
for (var i = input.length-1; i >=0; i--) {
var randomIndex = Math.floor(Math.random()*(i+1));
var itemAtIndex = input[randomIndex];
input[randomIndex] = input[i];
input[i] = itemAtIndex;
}
return input;
} }
paginationArray(pageNo, pageSize, array) {
var offset = (pageNo - 1) * pageSize;
return (offset + pageSize >= array.length) ? array.slice(offset, array.length) : array.slice(offset, offset + pageSize);
}
/**
* 初始化数据
*/
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; // -------------- Template function ---------------
// --------------------------------------------------
// --------------------------------------------------
// --------------------------------------------------
//
// .-~~~~~~~~~-._ _.-~~~~~~~~~-.
// __.' ~. .~ `.__
// .'// \./ \\`.
// .'// | \\`.
// .'// .-~"""""""~~~~-._ | _,-~~~~"""""""~-. \\`.
// .'//.-" `-. | .-' "-.\\`.
// .'//______.============-.. \ | / ..-============.______\\`.
//.'______________________________\|/______________________________`.
//
// --------------------------------------------------
// showParticle( element_id ) 泡泡效果
// --------------------------------------------------
// showEndPatal() / stopEndPatal() 花瓣飘落结束动画
// --------------------------------------------------
// showCorrectPatal() 指定元素上面飘花
// --------------------------------------------------
// convertPercentToRadian() 将百分比转换为弧长 第一个参数是百分比,第二个参数是方向 true为逆时针,false为顺时针。用于ShapeCircle换圆弧
// --------------------------------------------------
// --------------------------------------------------
// --------------------------------------------------
// --------------------------------------------------
// --------------------------------------------------
// --------------------------------------------------
const label1 = new Label();
label1.text = this.data.text;
label1.textAlign = 'center';
label1.fontSize = 50;
label1.fontName = 'BRLNSDB';
label1.fontColor = '#ffffff';
pic1.addChild(label1); // 泡泡
showParticle(card) {
let myCard = this.g_cartoon.getCartoonElementRelativePosition(card.id)
showPopParticle(this.g_cartoon.images.get("bubble"), { x: myCard.x , y: myCard.y }, this.g_stage);
}
// 选择正确动画
showCorrectPatal(card_id, showTime) {
this.m_elementPetalArr = [];
this.m_showElementPetalFlag = true;
this.addCorrectPetal(card_id);
setTimeout(()=>{
this.m_elementPetalArr = [];
this.m_showElementPetalFlag = false;
},showTime)
}
addCorrectPetal(card_id) {
if (!this.m_showElementPetalFlag) {
return;
}
let element = this.g_cartoon.getCartoonElement(card_id)
const petal = new MySprite(this.g_ctx);
const id = Math.ceil(Math.random() * 3);
petal.init(this.g_cartoon.images.get(this.m_PetalImage + id));
const pic2 = new MySprite(); const randomS = (Math.random() * 0.4 + 0.6) * this.g_mapScale * 0.5;
pic2.init(this.images.get(this.data.pic_url_2)); petal.setScaleXY(randomS);
pic2.x = this.canvasWidth / 2 + this.canvasWidth;
pic2.y = this.canvasHeight / 2;
pic2.setScaleXY(maxW / pic2.width);
this.renderArr.push(pic2); const randomR = Math.random() * 360;
this.pic2 = pic2; petal.rotation = randomR;
this.curPic = pic1; const randomX = Math.random() * element.ref.width * this.g_mapScale;
}
petal.x = element.ref.x - (element.ref.width * this.g_mapScale / 2) + randomX;
petal.y = element.ref.y - (element.ref.height * this.g_mapScale / 2);
btnLeftClicked() { const randomT = 1 + Math.random() * 2;
petal["time"] = randomT;
this.lastPage(); let randomTR = 360 * Math.random(); // - 180;
if (Math.random() < 0.5) {
randomTR *= -1;
} }
petal["tr"] = randomTR;
btnRightClicked() { this.m_elementPetalArr.push(petal);
moveItem(
this.nextPage(); petal,
petal.x,
element.ref.y + (element.ref.height * this.g_mapScale / 2),
petal["time"],
() => {
removeItemFromArr(this.m_elementPetalArr, petal);
}
);
rotateItem(petal, petal["tr"], petal["time"]);
setTimeout(() => {
this.addCorrectPetal(card_id);
}, 200);
} }
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, () => { showEndPatal() {
this.canTouch = true; this.m_endPageArr = [];
this.curPic = this.pic1; this.m_showPetalFlag = true;
}); this.addPetal();
} }
nextPage() { stopEndPatal() {
this.m_endPageArr = [];
this.m_showPetalFlag = false;
}
if (this.curPic == this.pic2) { addPetal() {
if (!this.m_showPetalFlag) {
return; return;
} }
const petal = this.getPetal();
this.canTouch = false; this.m_endPageArr.push(petal);
moveItem(
const moveLen = this.canvasWidth; petal,
tweenChange(this.pic1, {x: this.pic1.x - moveLen}, 1); petal.x,
tweenChange(this.pic2, {x: this.pic2.x - moveLen}, 1, () => { this.g_canvasHeight + petal.height * petal.scaleY,
this.canTouch = true; petal["time"],
this.curPic = this.pic2; () => {
}); removeItemFromArr(this.m_endPageArr, petal);
} }
);
pic1Clicked() { rotateItem(petal, petal["tr"], petal["time"]);
this.playAudio(this.data.audio_url); setTimeout(() => {
this.addPetal();
}, 100);
} }
pic2Clicked() { getPetal() {
this.playAudio(this.data.audio_url_2); const petal = new MySprite(this.g_ctx);
}
const id = Math.ceil(Math.random() * 3);
petal.init(this.g_cartoon.images.get(this.m_PetalImage + id));
const randomS = (Math.random() * 0.4 + 0.6) * this.g_mapScale;
petal.setScaleXY(randomS);
const randomR = Math.random() * 360;
petal.rotation = randomR;
const randomX = Math.random() * this.g_canvasWidth;
petal.x = randomX;
petal.y = (-petal.height / 2) * petal.scaleY;
mapDown(event) { const randomT = 2 + Math.random() * 5;
petal["time"] = randomT;
if (!this.canTouch) { let randomTR = 360 * Math.random(); // - 180;
return; if (Math.random() < 0.5) {
randomTR *= -1;
} }
petal["tr"] = randomTR;
if ( this.checkClickTarget(this.btnLeft) ) { return petal;
this.btnLeftClicked();
return;
} }
if ( this.checkClickTarget(this.btnRight) ) {
this.btnRightClicked();
return;
}
if ( this.checkClickTarget(this.pic1) ) { showJellyAnimation(element_id){
this.pic1Clicked(); let element = this.g_cartoon.getCartoonElement(element_id).ref
return; tweenChange(element,{ scaleX: this.g_mapScale * 1.1 , scaleY: this.g_mapScale * 1.1 }, 0.1, ()=>{
tweenChange(element,{ scaleX: this.g_mapScale * 0.9 , scaleY: this.g_mapScale * 0.9 }, 0.1, ()=>{
tweenChange(element,{ scaleX: this.g_mapScale , scaleY: this.g_mapScale }, 0.1 )
})
})
} }
if ( this.checkClickTarget(this.pic2) ) { showShakeAnimation(element_id){
this.pic2Clicked(); let element = this.g_cartoon.getCartoonElement(element_id).ref
return; let originX = element.x
tweenChange(element,{ x: originX - 10*this.g_mapScale }, 0.1, ()=>{
tweenChange(element,{ x: originX + 10*this.g_mapScale }, 0.1, ()=>{
tweenChange(element,{ x: originX }, 0.1 )
})
})
} }
convertPercentToRadian(per, direction){
let radian = 0;
if(direction) { //逆时针
if(per*360 > 270){
radian = 361 - (per*360 - 270)
}else{
radian = 270 - per*360
} }
}else{ //顺时针
mapMove(event) { if(per*360 > 90){
radian = per*360 - 90
}else{
radian = 270 + per*360
} }
mapUp(event) {
} }
return (radian * Math.PI) / 180;
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);
} }
} }
const res = [ const localImages = {
'startBtn': "assets/play/btn-start.png",
'restartBtn': "assets/play/btn-restart.png",
'petal_1': "assets/play/scrap-pic-1.png",
'petal_2': "assets/play/scrap-pic-2.png",
'petal_3': "assets/play/scrap-pic-3.png",
'bg_1280_720_Ruler': "assets/play/1280_720_Ruler.png",
'bubble': "assets/play/bubble.png",
'replay': 'assets/play/replay.png',
'title': 'assets/play/multiple-choice.png',
'group': 'assets/play/group.png',
'rectangle-3': 'assets/play/rectangle-3.png',
'drop-down': 'assets/play/drop-down.png',
'combined-shape': 'assets/play/combined-shape.png',
'choice-card-typeA': 'assets/play/choice-card-a.png',
'choice-card-typeB': 'assets/play/choice-card-b.png',
'choice-card-typeC': 'assets/play/choice-card-c.png',
'choice-card-typeD': 'assets/play/choice-card-d.png',
'complete-card': 'assets/play/complete-card.png',
'header': 'assets/play/header.png',
'background': 'assets/play/background.png',
'question-image': 'assets/play/question-image.png',
'speaker-1': 'assets/play/speaker-1.png',
'correct-icon': 'assets/play/correct-icon.png',
'incorrect-icon': 'assets/play/incorrect-icon.png',
'red-circle': 'assets/play/combined-shape-red-circle.png',
'rectangle-4': 'assets/play/rectangle-4.png',
'me': 'assets/play/me.png',
'bg-240-180': 'assets/play/bg-240-180.png',
'bg-453-251': 'assets/play/bg-453-251.png',
't_right': "assets/play/right.png",
't_arrow': "assets/play/arrow.png",
'bg_500_600': "assets/play/bg_500_600.png",
'bg_1280_222': "assets/play/bg_1280_222.png",
'bg_ready': "assets/play/bg_ready.png",
'ready': "assets/play/ready.png",
'go': "assets/play/go.png"
};
// ['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"],
]; const localAudios = {
'sm-back': "assets/audio/sm-back.mp3",
'sm-display': "assets/audio/sm-display.mp3",
'sm-wrong': "assets/audio/sm-wrong.mp3",
'sm-win': "assets/audio/sm-win.mp3",
'sm-in': "assets/audio/sm-in.mp3",
'sm-out': "assets/audio/sm-out.mp3",
'sm-click': "assets/audio/sm-click.mp3",
'sm-start': "assets/audio/sm-start.mp3",
'sm-correct': 'assets/audio/sm-correct.mp3',
'sm-choice-complete': 'assets/audio/sm-choice-complete.mp3',
'sm-choice-correct': 'assets/audio/sm-choice-correct.mp3',
'sm-choice-incorrect': 'assets/audio/sm-choice-error.mp3',
'sm-choice-in': 'assets/audio/sm-choice-in.mp3',
'sm-choice-show-answer': 'assets/audio/sm-choice-show-answer.mp3',
'sm-choice-timeup-0': 'assets/audio/sm-choice-timeup-0.mp3',
'sm-choice-timeup-3': 'assets/audio/sm-choice-timeup-3.mp3',
'sm-go': 'assets/audio/sm-go.mp3',
'sm-ready': 'assets/audio/sm-ready.mp3'
};
const resAudio = [ export {localImages, localAudios};
['click', "assets/play/music/click.mp3"],
];
export {res, resAudio};
import {
MySprite,
getMinScale,
ShapeRect,
tweenChange,
randomSortByArr,
Label,
showPopParticle,
moveItem,
removeItemFromArr,
rotateItem
} from "./Unit";
const myUtility = {};
// 系统缩放比例
myUtility.mapScale = 1;
// 音乐 和 图片的缓冲区
myUtility.audio = new Map();
myUtility.images = new Map();
myUtility.imagesOriginSize = new Map();
// 坐标原点 包含缩放
myUtility.originX = 0;
myUtility.originY = 0;
myUtility.setOrigin = (x, y)=>{
myUtility.originX = x;
myUtility.originY = y;
}
myUtility.getOrigin = ()=>{
return {
x: myUtility.originX,
y: myUtility.originY
}
}
// 坐标原点 包含缩放
myUtility.relativeOriginX = 0;
myUtility.relativeOriginY = 0;
myUtility.setRelativeOrigin = (x, y)=>{
myUtility.relativeOriginX = x;
myUtility.relativeOriginY = y;
}
myUtility.getRelativeOrigin = ()=>{
return {
x: myUtility.relativeOriginX,
y: myUtility.relativeOriginY
}
}
// 存放音乐和图片的地址
const audioObj = {}
const imageObj = {}
// 添加音乐
myUtility.audio.add = ( key, url ) => {
audioObj[key] = url
};
// 添加音乐
myUtility.images.add = ( key, url ) => {
imageObj[key] = url
};
// 播放音乐
myUtility.audio.playAudio = function(key, now = false, callback = null) {
const audio = myUtility.audio.get(key);
if (audio) {
if (now) {
audio.pause();
audio.currentTime = 0;
}
if (callback) {
audio.onended = () => {
callback();
};
}
audio.play();
}
}
// 异步加载图片 音频资源
myUtility.loadResources = ()=> {
const pr = [];
for(let key in imageObj){
const p = preloadImage(imageObj[key]).then(img => {
myUtility.images.set(key, img);
myUtility.imagesOriginSize.set(key, {width:img.width, height:img.height});
}).catch(err => console.log(key));
pr.push(p);
};
for(let key in audioObj){
const a = preloadAudio(audioObj[key]).then((music) => {
myUtility.audio.set(key, music);
}).catch(err => console.log(key));
pr.push(a);
};
return Promise.all(pr);
}
// 预加载图片
const preloadImage = (url) => {
return new Promise((resolve, reject) => {
const img = new Image();
img.src = url;
img.onload = () => resolve(img);
img.onerror = reject;
img.src = url;
});
}
// 预加载音频
const preloadAudio = (url) => {
return new Promise((resolve, reject) => {
const audio = new Audio();
audio.oncanplay = a => {
resolve(audio);
};
audio.onerror = () => {
reject();
};
audio.src = url;
audio.load();
});
}
// 缓存页面元素
myUtility.cartoonElementsBuffer = {}
myUtility.createCartoonElement = (key, element, width, heigth, initX, initY)=>{
myUtility.cartoonElementsBuffer[key] = {}
myUtility.cartoonElementsBuffer[key].id = key;
myUtility.cartoonElementsBuffer[key].width = width;
myUtility.cartoonElementsBuffer[key].heigth = heigth;
myUtility.cartoonElementsBuffer[key].initX = initX;
myUtility.cartoonElementsBuffer[key].initY = initY;
myUtility.cartoonElementsBuffer[key].ref = element;
return myUtility.cartoonElementsBuffer[key]
}
myUtility.getCartoonElementRef = (key)=>{
if(myUtility.cartoonElementsBuffer[key]){
return myUtility.cartoonElementsBuffer[key].ref;
}else{
return undefined
}
}
myUtility.getCartoonElement = (key)=>{
return myUtility.cartoonElementsBuffer[key]
}
myUtility.setCartoonElementPosition = (key, posi)=>{
myUtility.cartoonElementsBuffer[key].ref.x = posi.x
myUtility.cartoonElementsBuffer[key].ref.y = posi.y
myUtility.cartoonElementsBuffer[key].x = posi.x
myUtility.cartoonElementsBuffer[key].y = posi.y
}
myUtility.setCartoonElementPositionX = (key, x)=>{
myUtility.cartoonElementsBuffer[key].ref.x = x
myUtility.cartoonElementsBuffer[key].x = x
}
myUtility.setCartoonElementRelativePositionX = (key, x)=>{
myUtility.cartoonElementsBuffer[key].relativeX = x
}
myUtility.setCartoonElementPositionY = (key, y)=>{
myUtility.cartoonElementsBuffer[key].ref.y = y
myUtility.cartoonElementsBuffer[key].y = y
}
myUtility.setCartoonElementRelativePositionY = (key, y)=>{
myUtility.cartoonElementsBuffer[key].relativeY = y
}
myUtility.getCartoonElementPosition = (key)=>{
return {
x: myUtility.cartoonElementsBuffer[key].x,
y: myUtility.cartoonElementsBuffer[key].y
}
}
myUtility.getCartoonElementRelativePosition = (key)=>{
return {
x: myUtility.cartoonElementsBuffer[key].relativeX,
y: myUtility.cartoonElementsBuffer[key].relativeY
}
}
myUtility.animation = {}
myUtility.animation.move = (eleKey, distPosition, runTime, callback )=>{
tweenChange(myUtility.cartoonElementsBuffer[eleKey].ref, distPosition, runTime, ()=>{
callback && callback()
});
}
myUtility.animation.scaleChange = myUtility.animation.move;
export default myUtility
@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;
}
.anticon{
vertical-align: .1em!important;
}
<!doctype html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8" />
<title>NgOne</title> <title>NgOne</title>
<base href="/"> <base href="/" />
<!-- <meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">--> <style>
<meta name="viewport" content="width=device-width, initial-scale=1"> html, body{
<link rel="icon" type="image/x-icon" href="favicon.ico"> width: 100%;
<script type="text/javascript" src="https://staging-teach.cdn.ireadabc.com/h5template/h5-static-lib/js/air.js"></script> height: 100%;
</head> }
<body> </style>
<app-root></app-root> <meta name="viewport" content="width=device-width, initial-scale=1" />
</body> <link rel="icon" type="image/x-icon" href="favicon.ico" />
<!-- <script type="text/javascript" src="http://teach.cdn.ireadabc.com/h5template/h5-static-lib/js/air.js" ></script> -->
<script type="text/javascript" src="https://staging-teach.cdn.ireadabc.com/h5template/h5-static-lib/js/air_online.js"></script>
</head>
<body>
<app-root></app-root>
</body>
</html> </html>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>NgOne</title>
<base href="/">
<!-- <meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">-->
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<script type="text/javascript" src="https://staging-teach.cdn.ireadabc.com/h5template/h5-static-lib/js/air.js"></script>
</head>
<body>
Hello World!!
</body>
</html>
/* 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 */
@import "~ng-zorro-antd/src/ng-zorro-antd.css";
\ No newline at end of file
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