Commit 5411fff8 authored by liujiaxin's avatar liujiaxin

1

parent 8a51c964
...@@ -30,7 +30,10 @@ ...@@ -30,7 +30,10 @@
], ],
"styles": [ "styles": [
"./node_modules/ng-zorro-antd/ng-zorro-antd.min.css", "./node_modules/ng-zorro-antd/ng-zorro-antd.min.css",
"src/styles.css" "./node_modules/font-awesome/css/font-awesome.css",
"./node_modules/bootstrap/dist/css/bootstrap.min.css",
"./node_modules/animate.css/animate.min.css",
"src/styles.scss"
], ],
"scripts": [] "scripts": []
}, },
......
...@@ -24,6 +24,7 @@ import {AudioRecorderComponent} from './common/audio-recorder/audio-recorder.com ...@@ -24,6 +24,7 @@ import {AudioRecorderComponent} from './common/audio-recorder/audio-recorder.com
import { FontAwesomeModule, FaIconLibrary } from '@fortawesome/angular-fontawesome'; import { FontAwesomeModule, FaIconLibrary } from '@fortawesome/angular-fontawesome';
import { fas } from '@fortawesome/free-solid-svg-icons'; import { fas } from '@fortawesome/free-solid-svg-icons';
import { far } from '@fortawesome/free-regular-svg-icons'; import { far } from '@fortawesome/free-regular-svg-icons';
import { AngularCropperjsModule } from 'angular-cropperjs';
registerLocaleData(zh); registerLocaleData(zh);
...@@ -50,7 +51,8 @@ registerLocaleData(zh); ...@@ -50,7 +51,8 @@ registerLocaleData(zh);
FormsModule, FormsModule,
HttpClientModule, HttpClientModule,
BrowserAnimationsModule, BrowserAnimationsModule,
FontAwesomeModule FontAwesomeModule,
AngularCropperjsModule
], ],
providers: [ providers: [
{provide: ErrorHandler, useClass: MyErrorHandler}, {provide: ErrorHandler, useClass: MyErrorHandler},
......
@import '../style/common_mixin.css';
.model-content {
width: 100%;
height: 100%;
}
<div nz-row nzType="flex" nzGutter = '16' class="editor-reading-wrap">
<div class="model-content"> <div nz-col nzSpan="12">
<div class="upload-wrap" [style.display]="!picId ? 'block' : 'none'" #uploadImage>
<app-upload-image-with-preview
[picUrl]="picId"
(imageUploaded)="onImageUploadSuccess($event)"
></app-upload-image-with-preview>
</div>
<div class="edit-wrap" *ngIf="picId">
<!--<img [src]="picId | resource" alt="" #img>-->
<angular-cropper [cropperOptions]="cropperConfig"
[imageUrl]="picId"
#angularCropper></angular-cropper>
</div>
</div>
<div nz-col nzSpan="12">
<div nz-row>
<div nz-col nzSpan="24" style="text-align:right;">
<button nz-button
(click)="changeImage()"
style="margin-left: 1rem">
<i class="anticon anticon-upload"></i><span>Change Image</span>
</button>
<button nz-button
(click)="addPart()"
style="margin-left: 1rem">
<i class="anticon anticon-plus"></i><span>Add Crop</span>
</button>
<hr>
</div>
</div>
<div nz-row>
<div nz-col nzSpan="12"
#partConfigBoxes
*ngFor="let part of cropperParts;let i = index"
class="part-item" >
<div class="part-item-wrap" style="margin-right: 2.5rem;">
<i class="anticon anticon-edit"
nz-tooltip nzTitle="edit mode"
*ngIf="currentEditPartIndex !== i"
(click)="editPart(i)"></i>
<i class="anticon anticon-close-square dynamic-delete-button"
<div style="position: absolute; left: 200px; top: 100px; width: 800px;"> nz-popconfirm
nzTitle="Are you sure?"
(nzOnConfirm)="confirmRemovePart(i,$event)"
nz-tooltip></i>
<i class="anticon anticon-logout"
*ngIf="currentEditPartIndex === i"
<input type="text" nz-input [(ngModel)]="item.text" (blur)="save()"> nz-tooltip nzTitle="exit edit mode"
(click)="exitEditPart($event, i)"></i>
<app-upload-image-with-preview <div class="part-view-wrap">
[picUrl]="item.pic_url" <div class="part-view"
(imageUploaded)="onImageUploadSuccess($event, 'pic_url')" (click)="previewUserSelectPart(i)"
></app-upload-image-with-preview> [ngStyle]="part.previewCss">
<img *ngIf="currentEditPartIndex !== i"
[ngStyle]="part.imgCss"
(load)="initPart($event, i)"
[src]="picId">
</div>
</div>
<div class="part-text-wrap" >
<div *ngIf="currentEditPartIndex === i" style="height:2.5rem;">
<input nz-input
class="part-word"
[style.fontWeight] = "900"
nzSize="default"
autofocus
[(ngModel)]="part.word"
[value]="part.word"
(ngModelChange)="onChangePartWord($event, i)"
(keydown.enter)="savePart($event, i)"
(keydown.esc)="exitEditPart($event, i)"
>
<!--(blur)="savePart($event, i)"-->
<!--[(ngModel)]="part.word"-->
<!--(ngModelChange)="onChangePartWord($event, i)"-->
<!--(focus)="setCurrentInputField($event)"-->
<app-audio-recorder <i class="anticon anticon-check-square"
[audioUrl]="item.audio_url" nz-tooltip nzTitle="save"
(audioUploaded)="onAudioUploadSuccess($event, 'audio_url')" (click)="savePart($event,i)"></i>
></app-audio-recorder> </div>
<app-custom-hot-zone></app-custom-hot-zone> <div *ngIf="currentEditPartIndex !== i" class="text-word" style="">
<app-upload-video></app-upload-video> {{part.word}}
<app-lesson-title-config></app-lesson-title-config> </div>
</div>
</div>
</div>
</div>
</div> </div>
</div> </div>
\ No newline at end of file
.editor-reading-wrap{
input.err{
border-color: red;
}
//::ng-deep .cropper-wrapper {
// max-width: 100%!important;
// ::ng-deep .cropper{
//
// position: relative;
// padding-bottom: 56.25%;
// height: 0;
// overflow: hidden;
// }
//}
::ng-deep .cropper-wrapper {
max-width: 100%!important;
::ng-deep .cropper{
max-width: 100%!important;
width: 100%;
> img{
max-width: 100%!important;
}
}
}
.edit-wrap{
max-width: 100%;
min-width: 100%;
width: 100%;
position: relative;
img{
width: 100%;
}
}
.p-progress-bar {
position: relative;
width: 100%;
height: 0.3rem;
border: 1px solid #ccc;
border-radius: 1rem;
.p-progress-bg {
background-color: #1890ff;
border-radius: 1rem;
height: 100%;
}
.p-progress-value {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
text-shadow: 0 0 4px #000;
color: white;
text-align: center;
font-size: 0.9rem;
line-height: 1.5rem;
}
}
.parts-box{
.crop-preview-box{
border: 1px solid #ddd;
overflow: hidden;
box-shadow: 0 0 20px 0px gainsboro;
.crop-preview{
width: 100%;
padding-bottom: 56.25%;
//border: 1px solid #eee;
overflow: hidden;
//box-shadow: 0 0 20px 0px gainsboro;
position: relative;
margin: 0 auto;
}
}
}
.previewer{
border: 2px dashed #ddd;
//display: inline-block;
max-width: 100%;
width: 100%;
height: auto;
overflow: hidden;
}
.previewer img{
max-width: 100%;
}
.part-view-wrap{
width: 100%;
max-width: 100%;
height: auto;
overflow: hidden;
margin: 0 auto;
width: 100%;
height: 0;
padding-bottom: 56.25%;
position: relative;
margin-bottom: .5rem;
border: 1px solid #ddd;
overflow: hidden;
box-shadow: 0 0 20px 0px gainsboro;
.part-view{
cursor: pointer;
position: absolute;
top: 50%;
left: 50%;
transform: translateX(-50%) translateY(-50%);
width: 100%;
max-width: 100%;
height: auto;
overflow: hidden;
margin: 0 auto;
width: 100%;
height: 0;
padding-bottom: 56.25%;
img {
display: block;
min-width: 0px !important;
min-height: 0px !important;
max-width: none !important;
max-height: none !important;
}
}
}
.part-item{
float: none;
display: inline-block;
vertical-align: top;
margin-bottom: 1rem;
//padding-top: 1rem;
//border-top: 1px solid #ddd;
position: relative;
.part-text-wrap{
position: relative;
font-weight: 900;
.text-word{
//height:2.5rem;
line-height:2.5rem;
text-align: center;
word-wrap: break-word;
word-break: break-word;
font-size: 2rem;
}
}
.part-text{
text-align: center;
font-size: 2rem;
font-family: 'ChalkboardSE-Regular';
font-weight: 900;
}
i.anticon-edit, i.anticon-logout{
cursor: pointer;
position: absolute;
top: 4px;
font-size: 22px;
color: #1890ff;
transition: all .3s;
z-index: 1;
top: 2rem;
right: 1rem;
opacity: .7;
}
i.anticon-close-square{
cursor: pointer;
position: absolute;
top: 4px;
font-size: 24px;
color: #f5222d;
transition: all .3s;
z-index: 1;
top: 0rem;
right: 1rem;
opacity: .7;
}
i.anticon-check-square{
cursor: pointer;
position: absolute;
top: 4px;
font-size: 24px;
color: #1890ff;
transition: all .3s;
z-index: 1;
right: -1.5rem;
opacity: .7;
}
i.anticon-check-square:hover, i.anticon-edit:hover, i.anticon-close-square:hover {
opacity: 1;
}
}
}
import {Component, EventEmitter, Input, OnDestroy, OnChanges, OnInit, Output, ApplicationRef, ChangeDetectorRef} from '@angular/core'; import {
Component,
EventEmitter,
Input,
OnDestroy,
OnChanges,
OnInit,
Output,
ApplicationRef,
ChangeDetectorRef,
ViewChildren, QueryList, ElementRef, ViewChild, AfterViewInit
} from '@angular/core';
import { CropperComponent } from 'angular-cropperjs';
import { NzMessageService } from 'ng-zorro-antd';
import * as _ from 'lodash';
@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, AfterViewInit, OnDestroy {
// 储存数据用 // 储存数据用
saveKey = "test_0011"; saveKey = 'ww_reading';
// 储存对象 // 储存对象
item; item;
_tmpEditWord: string;
_tmpEditData: any;
constructor(private appRef: ApplicationRef,private changeDetectorRef: ChangeDetectorRef) { imageUploader: any;
uploading = false;
progress = 0;
// enableChangeImage = true;
picId = '';
@Output()
imageUploaded = new EventEmitter();
@Output()
update = new EventEmitter();
respipe: any;
cropperParts: Array<{
word: string,
data: any,
// imgData: any,
previewCss: any,
imgCss: any,
// color: string
}> = [];
currentEditPartIndex = -1;
colorIndex = 0;
@ViewChildren('partConfigBoxes')
partConfigBoxes: QueryList<ElementRef>;
@ViewChild('angularCropper')
angularCropper: CropperComponent;
@ViewChild('partConfigBox')
partConfigBox: ElementRef;
@ViewChild('uploadImage')
uploadImage: ElementRef;
@ViewChild('img')
img: ElementRef;
colors = [
'#4FC200',
'#00C0FF',
'#FFDC00',
'#FF8F00',
'#CF0000',
'#8E33D4',
'#FF0075'
];
cropperConfig = {
dragMode: 'move',
movable: true,
viewMode: 3,
autoCrop: true,
autoCropArea: .5,
rotatable: false,
zoomOnWheel: false,
zoomable: false,
toggleDragModeOnDblclick: false,
ready: this.initCropper.bind(this),
preview: '.crop-preview'
};
constructor(private appRef: ApplicationRef,
private nzMessageService: NzMessageService,
private changeDetectorRef: ChangeDetectorRef) {
} }
ngOnInit() { ngOnInit() {
this.item = {}; this.item = {
contentObj: {}
};
// 获取存储的数据 // 获取存储的数据
(<any> window).courseware.getData((data) => { (window as any).courseware.getData((data) => {
if (data) { if (data) {
this.item = data; this.item = data;
if (this.angularCropper) {
this.angularCropper.cropper.reset();
this.angularCropper.cropper.destroy();
}
this.picId = this.item.contentObj.pic_id;
this.currentEditPartIndex = -1;
this.cropperParts.length = 0;
setTimeout(() => {
// console.log(this.item.id , this.item.contentObj.parts)
const parts = _.get(this.item.contentObj, 'parts', []);
this.cropperParts = parts.map(item => {
const nitem = Object.assign({}, item);
nitem.previewCss = {};
nitem.imgCss = {};
return nitem;
});
});
} }
this.init(); this.init();
...@@ -54,25 +152,7 @@ export class FormComponent implements OnInit, OnChanges, OnDestroy { ...@@ -54,25 +152,7 @@ export class FormComponent implements OnInit, OnChanges, OnDestroy {
} }
/**
* 储存图片数据
* @param e
*/
onImageUploadSuccess(e, key) {
this.item[key] = e.url;
this.save();
}
/**
* 储存音频数据
* @param e
*/
onAudioUploadSuccess(e, key) {
this.item[key] = e.url;
this.save();
}
...@@ -80,7 +160,15 @@ export class FormComponent implements OnInit, OnChanges, OnDestroy { ...@@ -80,7 +160,15 @@ export class FormComponent implements OnInit, OnChanges, OnDestroy {
* 储存数据 * 储存数据
*/ */
save() { save() {
(<any> window).courseware.setData(this.item, null, this.saveKey); const data = this.cropperParts.map(item => {
const nitem = Object.assign({}, item);
delete nitem.previewCss;
delete nitem.imgCss;
return nitem;
});
this.item.contentObj.parts = data;
// this.item.contentObj['imgData'] = imgData;
(window as any).courseware.setData(this.item, null, this.saveKey);
this.refresh(); this.refresh();
} }
...@@ -92,6 +180,293 @@ export class FormComponent implements OnInit, OnChanges, OnDestroy { ...@@ -92,6 +180,293 @@ export class FormComponent implements OnInit, OnChanges, OnDestroy {
this.appRef.tick(); this.appRef.tick();
}, 1); }, 1);
} }
changeImage() {
// console.log(this.imageUploader);
this.imageUploader = this.uploadImage.nativeElement.querySelector('input[type="file"]');
// if (!this.imageUploader) {
// this.imageUploader = this.uploadImage.nativeElement.querySelector('input[type="file"]')
// if (!this.imageUploader.onclick) {
// const self = this;
// this.imageUploader.onclick = () => {
// document.body.addEventListener('focus', self.afterChangeImage.bind(self), true);
// };
// }
// }
this.imageUploader.click();
// const self = this;
// this.picId = 0;
}
onImageUploadSuccess(e) {
this.item.contentObj.pic_id = e.url;
this.picId = e.url;
this.update.emit(this.item);
if (this.angularCropper ) {
this.item.contentObj.pic_id = this.picId = e.url;
this.cropperParts.length = 0;
if (this.angularCropper && this.angularCropper.cropper) {
this.angularCropper.cropper.reset();
this.angularCropper.cropper.destroy();
this.colorIndex = 0;
}
}
}
getCss(data, target) {
const imgData = this.item.contentObj.imgData;
// const w = this.partConfigBox.nativeElement.offsetWidth;
// const w = this.partBoxTpl.nativeElement.offsetWidth;
// const part = this.partConfigBoxes.toArray()[this.currentEditPartIndex]
const w = target.clientWidth;
const h = target.clientHeight;
// let scale = w / data.width;
// if (data.height >= data.width) {
// scale = h / data.height;
// }
let scale = 1;
const c_w = w / data.width;
const c_h = h / data.height;
scale = Math.min(c_w, c_h);
const iw = scale * imgData.naturalWidth;
const ih = scale * imgData.naturalHeight;
const ix = scale * data.x;
const iy = scale * data.y;
const pw = scale * data.width;
const ph = scale * data.height;
return {iw, ih, ix, iy, pw, ph };
}
addPart() {
// console.log('addPart');
// const inv_idx = this.cropperParts.findIndex(item => {
// return !item.word || !item.word.trim();
// });
// if (inv_idx !== -1) {
// this.currentEditPartIndex = inv_idx;
// const el = this.partConfigBoxes.toArray()[inv_idx].nativeElement.querySelector('.part-word');
// if (!el.classList.contains('err')) {
// el.classList.add('err');
// }
// return;
// }
if (this.cropperParts.length === this.colors.length ) {
this.nzMessageService.info('you can only add seven parts.');
return;
}
// const last_color = this.cropperParts[this.cropperParts.length - 1].color;
// this.colorIndex = this.colors.findIndex((c) => {
// return last_color === c;
// });
// this.colorIndex++;
// const color = this.colors[this.colorIndex % this.colors.length];
this.cropperParts.push({
word: '',
data: this.angularCropper.cropper.getData(),
// imgData: imgData,
previewCss: { },
// `width:${pw}px;height:${ph}px;`,
imgCss: {},
// playerPmgCss: {},
// color: color
});
this.currentEditPartIndex = this.cropperParts.length - 1;
// this.angularCropper.cropper.reset();
this.focusUserInput();
this.save();
}
focusUserInput() {
setTimeout(() => {
this.partConfigBoxes.toArray()[this.currentEditPartIndex].nativeElement.querySelector('input').focus();
}, 10);
}
editPart(idx) {
this.currentEditPartIndex = idx;
const obj = this.cropperParts[idx];
this.angularCropper.cropper.setData(obj.data);
obj.previewCss = { };
obj.imgCss = { };
this._tmpEditWord = obj.word;
this._tmpEditData = Object.assign({}, obj.data);
this.focusUserInput();
}
confirmRemovePart(idx, evt) {
this.removePart(idx, evt);
}
removePart(idx, evt) {
this.cropperParts.splice(idx, 1);
this.save();
this.colorIndex -= 1;
if (this.colorIndex < 0 ) {
this.colorIndex = 0;
}
}
exitEditPart(evt, idx) {
// if( !this.cropperParts[idx].word) {
// const el = this.partConfigBoxes.toArray()[idx].nativeElement.querySelector('.part-word');
// if (!el.classList.contains('err')) {
// el.classList.add('err');
// }
// return;
// }
// if (!this._tmpEditWord) {
// this._tmpEditWord = this.cropperParts[idx].word;
// }
this.currentEditPartIndex = -1;
this.cropperParts[idx].word = this._tmpEditWord;
if (this._tmpEditData) {
this.cropperParts[idx].data = this._tmpEditData;
this.angularCropper.cropper.setData(this._tmpEditData);
}
this._tmpEditWord = '';
this._tmpEditData = {};
const cropper: any = this.angularCropper.cropper;
cropper.previews = [];
}
savePart(evt, idx) {
// const el = this.partConfigBoxes.toArray()[idx].nativeElement.querySelector('.part-word');
// if (!el.value) {
// if (!el.classList.contains('err')) {
// el.classList.add('err');
// }
// return;
// }
// if (el && el.classList.contains('err')) {
// return;
// }
this.currentEditPartIndex = this.cropperParts.length - 1;
this.currentEditPartIndex = -1;
// const origin = this.cropperParts[idx];
const data = this.angularCropper.cropper.getData();
this.cropperParts[idx].data = data;
const cropper: any = this.angularCropper.cropper;
cropper.previews = [];
this.save();
}
previewUserSelectPart(idx) {
const part = this.cropperParts[idx];
this.angularCropper.cropper.setData(part.data);
}
initPart(evt, idx) {
// console.log('initPart', evt);
const part = this.cropperParts[idx];
const img = evt.target;
const preview = img.parentNode;
if (!preview) {
return;
}
const {iw, ih, ix, iy, pw, ph } = this.getCss(part.data, preview);
part.previewCss = {
'width.px': pw,
'height.px': ph,
'padding-bottom': 0
};
part.imgCss = {
'width.px': iw,
'height.px': ih,
transform: `translateX(-${ix}px) translateY(-${iy}px)`
};
}
onChangePartWord(val, idx ) {
// const part = this.cropperParts[idx];
const el: any = this.partConfigBoxes.toArray()[idx].nativeElement.querySelector('.part-word');
if (!val) {
if (el && el.classList.contains('err')) {}
el.classList.add('err');
} else {
if (el) {
el.classList.remove('err');
}
}
}
ngAfterViewInit() {
// console.log('ngAfterViewInit');
this.partConfigBoxes.changes.subscribe(t => {
const part = this.partConfigBoxes.toArray()[this.currentEditPartIndex];
if (part) {
const p = part.nativeElement.querySelector('.part-view');
p.innerHTML = '';
// debugger
/*
const img = <HTMLElement>this.angularCropper.imageElement.cloneNode();
img.className = 'crop-part-preview';
img.style.cssText = (
`display: block;
position: absolute;
width: 100%;
min-width: 0;
min-height: 0;
max-width: none;
max-height: none;`
);
p.appendChild(img);
*/
// const cfg = Object.assign({}, this.cropperConfig);
// cfg['preview'] = p;
const cropper: any = this.angularCropper.cropper;
if (!cropper.viewBox) {
return;
}
cropper.viewBox.innerHTML = '';
cropper.options.preview = p;
cropper.initPreview();
cropper.preview();
p.style.padding = '0';
// this.setPreviewImage();
}
});
// this.enableChangeImage = true;
}
initCropper(evt) {
// this.enableChangeImage = true;
// this.angularCropper.cropper.setDragMode('move');
// console.log('initCropper save imgData');
this.item.contentObj.imgData = this.angularCropper.cropper.getImageData();
// console.log(this.item.contentObj['imgData']);
// this.item.contentObj['parts'] = [];
// this.previewPart.nativeElement.style.padding = '0';
// console.log('initCropper', pic, this.item.contentObj.pic_id);
if (evt.target.src === this.item.contentObj.pic_id) {
} else {
// Ken 2018-09-29 14:54 不同时说明是重新上传的, 需要保存
this.save();
}
}
} }
import {AfterViewInit, HostListener} from '@angular/core';
export class BaseResizeComponent implements AfterViewInit {
resizeTimer: any;
constructor() {
}
ngAfterViewInit() {
this.autoSizeText();
}
@HostListener('window:resize', ['$event'])
onResize(event) {
clearTimeout(this.resizeTimer);
this.resizeTimer = setTimeout(() => {
this.autoSizeText();
}, 500);
}
resizeText(el: any, fontSize: number = 10) {
fontSize++; // el.computedStyleMap().get('font-size');
// Ken 2018-08-09 14:39 无文字时, 不需要计算字号
if (el.textContent.length === 0) {
return;
}
el.style.fontSize = (fontSize + 2) + 'px';
if (el.scrollHeight > el.parentElement.offsetHeight) {
el.style.fontSize = fontSize + 'px';
} else {
setTimeout(() => {
this.resizeText(el, fontSize);
}, 32);
}
}
autoSizeText() {
const elements: NodeListOf<HTMLElement> = document.querySelectorAll('.fit-font-size');
for (let i = 0; i < elements.length; i++) {
const el = elements.item(i);
this.resizeText(el);
}
}
}
.game-container {
width: 100%;
height: 100%;
background: #ffffff;
background-size: cover;
}
#canvas {
}
@font-face
{
font-family: 'BRLNSDB';
src: url("../../assets/font/BRLNSDB.TTF") ;
}
<div class="game-container" #wrap> <div class="player-reading" #readingContainer>
<canvas id="canvas" #canvas></canvas> <div class="imgDetailBox"
(click)="closeDetailView($event)"
[style.display]="partDetailVisible? 'block' : 'none'">
<div class="detail-preview-box" [ngStyle]="partViewDetailCss" >
<img [src]="data.contentObj.pic_id | resource" [ngStyle]="partViewImgDetailCss">
</div>
</div>
<div class="fill">
<div class="row fill" style="margin: 0 6vh;padding-top:3vh;padding-bottom: 3vh;">
<div class="col-6">
<div class="bar">
<div class="parts-wrap">
<div class="parts" #previewParts>
<div class="row" *ngFor="let part of parts;let i = index">
<div class="col-6 word-wrap" [style.color]="part.color">
<span class="word " [style.color]="part.color">
{{part.word}}
</span>
</div>
<div class="col-6">
<div class="part-pic-box-border" (click)="highlightSelect(i)" [style.borderColor]="part.color">
<div class="part-pic-box" [ngStyle]="part.previewCss">
<img [src]="data.contentObj.pic_id | resource" [ngStyle]="part.imgCss">
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-6" style="display: flex;justify-content: center;align-items: center;">
<div class="pic-preview" [ngStyle]="previewContainerCss" #previewContainer>
<img [src]="data.contentObj.pic_id | resource" alt="">
<div class="highlight" #highlightBox>
<div class="t-b"></div>
<div class="r-b"></div>
<div class="b-b"></div>
<div class="l-b"></div>
</div>
</div>
</div>
</div>
</div>
</div> </div>
.player-reading{
background: url("../../assets/reading/bg_reading.png") 50% 50% no-repeat;
width: 100%;
height: 100%;
position: relative;
//.container{
// padding-left: 2.5vh;
// padding-right: 2.5vh;
// .col-6{
// padding-left: 2.5vh;
// padding-right: 2.5vh;
// }
//}
.imgDetailBox{
width: 50%;
height: 100%;
position: absolute;
display: none;
z-index: 9999;
background: #fff;
overflow: hidden;
box-shadow: 0 0 20px 0px #444;
left: 2vh;
.close-detail-view{
position: absolute;
top: 1rem;
right: 1rem;
position: absolute;
font-size: 2rem;
cursor: pointer;
opacity: .7;
}
.close-detail-view:hover{
opacity: 1;
}
.detail-preview-box{
//display: flex;
//justify-content: center;
//align-items: center;
max-height: 100%;
max-width: 100%;
width: auto;
height: auto;
overflow: hidden;
position: absolute;
top: 50%;
transform: translateX(-50%) translateY(-50%);
left: 50%;
overflow: hidden;
img{
max-width: 100%;
max-height: 100%;
width: auto;
height: auto;
}
}
.detail-preview-box.c0{border: 3px solid #4FC200;}
.detail-preview-box.c1{border: 3px solid #00C0FF;}
.detail-preview-box.c2{border: 3px solid #FFDC00;}
.detail-preview-box.c3{border: 3px solid #FF8F00;}
.detail-preview-box.c4{border: 3px solid #CF0000;}
.detail-preview-box.c5{border: 3px solid #8E33D4;}
.detail-preview-box.c6{border: 3px solid #FF0075;}
}
.fill{
min-height: 100%;
height: 100%;
}
.bar {
height: 100%;
overflow-y: auto;
overflow-x: hidden;
width: calc(100% - 30px);
position: absolute;
z-index: 10;
.parts-wrap{
display: table;
height: 100%;
width: 100%;
.parts{
display: table-cell;
vertical-align: middle;
}
}
}
.parts-wrap{
height: 100%;
overflow-y: auto;
overflow-x: hidden;
position: relative;
.parts{
//position: absolute;
//width: 100%;
//top: 50%;
//transform: translateY(-50%);
//
//display: grid;
//grid-template-columns: repeat(1, 1fr);
//grid-column-gap: .3rem;
//grid-row-gap: .3rem;
//grid-auto-rows: 16vh;
//.box-row{
// position: relative;
// display: block;
// width: 100%;
// overflow: hidden;
//}
.row{
max-height: 16vh;
min-height: 16vh;
height: 16vh;
}
.word-wrap{
word-wrap: break-word;
word-break: break-word;
position: relative;
//white-space: nowrap;
//display: table;
.word{
//display: table-cell;
//vertical-align: middle;
//margin: 0 auto;
font-weight: 900;
font-size: 2.5vh;
font-family: 'ChalkboardSE-Regular';
position: absolute;
top: 50%;
transform: translateX(-50%) translateY(-50%);
left: 50%;
width: calc( 100% - 30px);
display: block;
word-wrap: break-word;
word-break: break-word;
overflow-wrap: normal;
//white-space: nowrap;
//white-space: pre-wrap;
text-align: left;
}
}
.part-pic-box-border{
cursor: pointer;
max-width: calc(100% - 1vh);
overflow: hidden;
margin: 0 auto;
border: .5vh solid #333;
border-radius: 1vh;
max-height: calc(100% - 1vh);
position: relative;
display: block;
width: calc(100% - 1vh);
height: 0;
padding-bottom: 56.25%;
top: 50%;
transform: translateY(-50%);
.part-pic-box{
position: absolute;
top: 50%;
transform: translateX(-50%) translateY(-50%);
left: 50%;
overflow: hidden;
}
}
}
}
.pic-preview{
object-fit: contain;
position: relative;
//display: initial;
//top: 50%;
//left: 50%;
//transform: translateY(-50%) translateX(-50%);
max-height: 100%;
max-width: 100%;
//margin-top: 1rem;
//margin-bottom: 1rem;
//height: calc(100% - 2rem);
//max-width: calc(100% - 30px);
//border: 1px solid #666;
overflow: hidden;
box-shadow: 0 0 20px 0px #444;
img{
//object-fit: contain;
//display: flex;
max-width: 100%;
width: auto;
height: auto;
max-height: 100%;
}
.highlight.c0 {
.t-b,.r-b,.l-b,.b-b{
background: #4FC200!important;
}
}
.highlight.c1 {
.t-b,.r-b,.l-b,.b-b{
background: #00C0FF!important;
}
}
.highlight.c2 {
.t-b,.r-b,.l-b,.b-b{
background: #FFDC00!important;
}
}
.highlight.c3 {
.t-b,.r-b,.l-b,.b-b{
background: #FF8F00!important;
}
}
.highlight.c4 {
.t-b,.r-b,.l-b,.b-b{
background: #CF0000!important;
}
}
.highlight.c5 {
.t-b,.r-b,.l-b,.b-b{
background: #8E33D4!important;
}
}
.highlight.c6 {
.t-b,.r-b,.l-b,.b-b{
background: #FF0075!important;
}
}
.highlight{
position: absolute;
z-index: 100;
.t-b{
position: absolute;
width: 100%;
height: 2px ;
top: 0;
background: red;
}
.r-b{
position: absolute;
height: calc(100% - 2px);
width: 2px ;
right: 0;
background: red;
}
.b-b{
position: absolute;
width: 100%;
height: 2px ;
bottom: 0;
background: red;
}
.l-b{
position: absolute;
height: calc(100% - 2px);
width: 2px ;
left: 0;
background: red;
}
}
}
}
import {Component, ElementRef, ViewChild, OnInit, Input, OnDestroy, HostListener} from '@angular/core'; import { Component, ElementRef, ViewChild, OnInit, Input, OnDestroy, HostListener, AfterViewInit } from '@angular/core';
import { import {
Label, Label,
...@@ -11,669 +11,244 @@ import {Subject} from 'rxjs'; ...@@ -11,669 +11,244 @@ import {Subject} from 'rxjs';
import {debounceTime} from 'rxjs/operators'; import {debounceTime} from 'rxjs/operators';
import TWEEN from '@tweenjs/tween.js'; import TWEEN from '@tweenjs/tween.js';
import { BaseResizeComponent } from './base-resize.component';
@Component({ @Component({
selector: 'app-play', selector: 'app-play',
templateUrl: './play.component.html', templateUrl: './play.component.html',
styleUrls: ['./play.component.css'] styleUrls: ['./play.component.scss']
}) })
export class PlayComponent implements OnInit, OnDestroy { export class PlayComponent extends BaseResizeComponent implements OnInit, AfterViewInit {
@ViewChild('canvas', {static: true }) canvas: ElementRef;
@ViewChild('wrap', {static: true }) wrap: ElementRef;
saveKey = 'ww_reading';
// 数据 // 数据
data; data;
parts: any;
ctx; @ViewChild('previewParts', {static: true})
previewParts: ElementRef;
canvasWidth = 1280; // canvas实际宽度 @ViewChild('highlightBox', {static: true})
canvasHeight = 720; // canvas实际高度 highlightBox: ElementRef;
@ViewChild('previewContainer', {static: true})
canvasBaseW = 1280; // canvas 资源预设宽度 previewContainer: ElementRef;
canvasBaseH = 720; // canvas 资源预设高度 @ViewChild('readingContainer', {static: true})
readingContainer: ElementRef;
mx; // 点击x坐标 imgData: any;
my; // 点击y坐标 previewContainerCss: any;
colors = [
'#4FC200',
// 资源 '#00C0FF',
rawImages = new Map(res); '#FFDC00',
rawAudios = new Map(resAudio); '#FF8F00',
'#CF0000',
images = new Map(); '#8E33D4',
'#FF0075'
animationId: any; ];
winResizeEventStream = new Subject(); partViewDetailCss: any;
partViewImgDetailCss: any;
audioObj = {}; partDetailVisible = false;
cw: number;
renderArr; ch: number;
mapScale = 1;
constructor() {
canvasLeft; super();
canvasTop;
saveKey = 'test_0011';
btnLeft;
btnRight;
pic1;
pic2;
canTouch = true;
curPic;
@HostListener('window:resize', ['$event'])
onResize(event) {
this.winResizeEventStream.next();
} }
ngOnInit() { ngOnInit() {
this.data = {}; this.data = {
contentObj: {}
};
// 获取数据 // 获取数据
const getData = (<any> window).courseware.getData; const getData = (window as any).courseware.getData;
getData((data) => { getData((data) => {
if (data && typeof data == 'object') { if (data && typeof data === 'object') {
this.data = data; this.data = data;
} }
// console.log('data:' , data); this.init();
// @ts-ignore
// 初始化 各事件监听 if (window.air) {
this.initListener(); // @ts-ignore
window.air.hideAirClassLoading(this.saveKey, this.data);
// 若无数据 则为预览模式 需要填充一些默认数据用来显示 }
this.initDefaultData();
// 初始化 音频资源
this.initAudio();
// 初始化 图片资源
this.initImg();
// 开始预加载资源
this.load();
}, this.saveKey); }, this.saveKey);
} }
ngOnDestroy() {
window['curCtx'] = null;
window.cancelAnimationFrame(this.animationId);
}
load() {
// 预加载资源
this.loadResources().then(() => {
window["air"].hideAirClassLoading(this.saveKey, this.data);
this.init();
this.update();
});
}
init() { init() {
// console.log('ngOnInit', this.sid, this.data);
this.initCtx(); this.highlightBox.nativeElement.style.display = 'none';
this.initData(); this.imgData = this.data.contentObj.imgData;
this.initView(); if (!this.imgData) {
}
initCtx() {
this.canvasWidth = this.wrap.nativeElement.clientWidth;
this.canvasHeight = this.wrap.nativeElement.clientHeight;
this.canvas.nativeElement.width = this.wrap.nativeElement.clientWidth;
this.canvas.nativeElement.height = this.wrap.nativeElement.clientHeight;
this.ctx = this.canvas.nativeElement.getContext('2d');
this.canvas.nativeElement.width = this.canvasWidth;
this.canvas.nativeElement.height = this.canvasHeight;
window['curCtx'] = this.ctx;
}
updateItem(item) {
if (item) {
item.update();
}
}
updateArr(arr) {
if (!arr) {
return; return;
} }
for (let i = 0; i < arr.length; i++) { if (this.imgData.naturalWidth > this.imgData.naturalHeight) {
arr[i].update(this); this.previewContainerCss = { /*'height': 'fit-content', */
} // 'height.px': this.imgData.naturalHeight ,
} // 'width.px': this.imgData.naturalWidth,
width: 'max-content'
initListener() {
this.winResizeEventStream
.pipe(debounceTime(500))
.subscribe(data => {
this.renderAfterResize();
});
// ---------------------------------------------
const setParentOffset = () => {
const rect = this.canvas.nativeElement.getBoundingClientRect();
this.canvasLeft = rect.left;
this.canvasTop = rect.top;
};
const setMxMyByTouch = (event) => {
if (event.touches.length <= 0) {
return;
}
if (this.canvasLeft == null) {
setParentOffset();
}
this.mx = event.touches[0].pageX - this.canvasLeft;
this.my = event.touches[0].pageY - this.canvasTop;
};
const setMxMyByMouse = (event) => {
this.mx = event.offsetX;
this.my = event.offsetY;
};
// ---------------------------------------------
let firstTouch = true;
const touchDownFunc = (e) => {
if (firstTouch) {
firstTouch = false;
removeMouseListener();
}
setMxMyByTouch(e);
this.mapDown(e);
};
const touchMoveFunc = (e) => {
setMxMyByTouch(e);
this.mapMove(e);
};
const touchUpFunc = (e) => {
setMxMyByTouch(e);
this.mapUp(e);
};
const mouseDownFunc = (e) => {
if (firstTouch) {
firstTouch = false;
removeTouchListener();
}
setMxMyByMouse(e);
this.mapDown(e);
};
const mouseMoveFunc = (e) => {
setMxMyByMouse(e);
this.mapMove(e);
};
const mouseUpFunc = (e) => {
setMxMyByMouse(e);
this.mapUp(e);
};
const element = this.canvas.nativeElement;
const addTouchListener = () => {
element.addEventListener('touchstart', touchDownFunc);
element.addEventListener('touchmove', touchMoveFunc);
element.addEventListener('touchend', touchUpFunc);
element.addEventListener('touchcancel', touchUpFunc);
};
const removeTouchListener = () => {
element.removeEventListener('touchstart', touchDownFunc);
element.removeEventListener('touchmove', touchMoveFunc);
element.removeEventListener('touchend', touchUpFunc);
element.removeEventListener('touchcancel', touchUpFunc);
};
const addMouseListener = () => {
element.addEventListener('mousedown', mouseDownFunc);
element.addEventListener('mousemove', mouseMoveFunc);
element.addEventListener('mouseup', mouseUpFunc);
};
const removeMouseListener = () => {
element.removeEventListener('mousedown', mouseDownFunc);
element.removeEventListener('mousemove', mouseMoveFunc);
element.removeEventListener('mouseup', mouseUpFunc);
};
addMouseListener();
addTouchListener();
}
playAudio(key, now = false, callback = null) {
const audio = this.audioObj[key];
if (audio) {
if (now) {
audio.pause();
audio.currentTime = 0;
}
if (callback) {
audio.onended = () => {
callback();
};
}
audio.play();
}
}
loadResources() {
const pr = [];
this.rawImages.forEach((value, key) => {// 预加载图片
const p = this.preload(value)
.then(img => {
this.images.set(key, img);
})
.catch(err => console.log(err));
pr.push(p);
});
this.rawAudios.forEach((value, key) => {// 预加载音频
const a = this.preloadAudio(value)
.then(() => {
// this.images.set(key, img);
})
.catch(err => console.log(err));
pr.push(a);
});
return Promise.all(pr);
}
preload(url) {
return new Promise((resolve, reject) => {
const img = new Image();
// img.crossOrigin = "anonymous";
img.onload = () => resolve(img);
img.onerror = reject;
img.src = url;
});
}
preloadAudio(url) {
return new Promise((resolve, reject) => {
const audio = new Audio();
audio.oncanplay = (a) => {
resolve();
}; };
audio.onerror = () => { } else {
reject(); this.previewContainerCss = { /*'width': 'fit-content', */
// 'height.px': this.imgData.naturalHeight,
// 'width.px': this.imgData.naturalWidth
height: 'max-content'
}; };
audio.src = url;
audio.load();
});
}
renderAfterResize() {
this.canvasWidth = this.wrap.nativeElement.clientWidth;
this.canvasHeight = this.wrap.nativeElement.clientHeight;
this.init();
}
checkClickTarget(target) {
const rect = target.getBoundingBox();
if (this.checkPointInRect(this.mx, this.my, rect)) {
return true;
} }
return false; // console.log(this.previewContainer.nativeElement.offsetHeight);
}
getWorlRect(target) {
let rect = target.getBoundingBox();
if (target.parent) {
const pRect = this.getWorlRect(target.parent);
rect.x += pRect.x;
rect.y += pRect.y;
}
return rect;
}
checkPointInRect(x, y, rect) {
if (x >= rect.x && x <= rect.x + rect.width) {
if (y >= rect.y && y <= rect.y + rect.height) {
return true;
}
}
return false;
} }
calcCssProp (data, w, h) {
// let scale = w / data.width;
// if (data.height >= data.width) {
// const h = w * 0.5625;
// scale = h / data.height;
// }
let scale = 1;
const c_w = w / data.width;
const c_h = h / data.height;
scale = Math.min(c_w, c_h);
addUrlToAudioObj(key, url = null, vlomue = 1, loop = false, callback = null) {
const audioObj = this.audioObj; const iw = scale * this.imgData.naturalWidth;
const ih = scale * this.imgData.naturalHeight;
const ix = scale * data.x;
const iy = scale * data.y;
if (url == null) { const pw = scale * data.width;
url = key; const ph = scale * data.height;
}
this.rawAudios.set(key, url);
const audio = new Audio();
audio.src = url;
audio.load();
audio.loop = loop;
audio.volume = vlomue;
audioObj[key] = audio;
if (callback) {
audio.onended = () => {
callback();
};
}
}
addUrlToImages(url) { return {iw, ih, ix, iy, pw, ph };
this.rawImages.set(url, url);
} }
getPreviewData(cw, ch, index) {
// const cw = this.previewContainer.nativeElement.offsetWidth;
// const ch = this.previewContainer.nativeElement.offsetHeight;
const part = this.parts[index];
let ratio = cw / this.imgData.naturalWidth;
if (part.data.height >= part.data.width) {
// ======================================================编写区域========================================================================== ratio = ch / this.imgData.naturalHeight;
/**
* 添加默认数据 便于无数据时的展示
*/
initDefaultData() {
if (!this.data.pic_url) {
this.data.pic_url = 'assets/play/default/pic.jpg';
this.data.pic_url_2 = 'assets/play/default/pic.jpg';
} }
const hl_w = Math.floor(part.data.width * ratio);
const hl_h = Math.floor(part.data.height * ratio);
const hl_x = Math.floor(part.data.x * ratio);
const hl_y = Math.floor(part.data.y * ratio);
return {hl_w, hl_h, hl_x, hl_y};
} }
highlightSelect(index) {
// console.log(this.previewContainer.nativeElement);
const cw = this.previewContainer.nativeElement.offsetWidth;
const ch = this.previewContainer.nativeElement.offsetHeight;
const {hl_w, hl_h, hl_x, hl_y} = this.getPreviewData(cw, ch, index);
// const cw = this.previewContainer.nativeElement.offsetWidth;
// const ch = this.previewContainer.nativeElement.offsetHeight;
// const part = this.parts[index];
// let ratio = cw / this.imgData.naturalWidth;
// if (part.data.height >= part.data.width) {
// ratio = ch / this.imgData.naturalHeight;
// }
// const hl_w = Math.floor(part.data.width * ratio);
// const hl_h = Math.floor(part.data.height * ratio);
// const hl_x = Math.floor(part.data.x * ratio);
// const hl_y = Math.floor(part.data.y * ratio);
/** this.highlightBox.nativeElement.className = this.highlightBox.nativeElement.className.replace(/c[\d]/, '');
* 添加预加载图片 this.highlightBox.nativeElement.classList.add('c' + index);
*/ this.highlightBox.nativeElement.style.width = hl_w + 'px';
initImg() { this.highlightBox.nativeElement.style.height = hl_h + 'px';
this.highlightBox.nativeElement.style.top = hl_y + 'px';
this.addUrlToImages(this.data.pic_url); this.highlightBox.nativeElement.style.left = hl_x + 'px';
this.addUrlToImages(this.data.pic_url_2); this.highlightBox.nativeElement.style.display = 'block';
}
/**
* 添加预加载音频
*/
initAudio() {
// 音频资源
this.addUrlToAudioObj(this.data.audio_url);
this.addUrlToAudioObj(this.data.audio_url_2);
// 音效
this.addUrlToAudioObj('click', this.rawAudios.get('click'), 0.3);
}
/**
* 初始化数据
*/
initData() {
const sx = this.canvasWidth / this.canvasBaseW;
const sy = this.canvasHeight / this.canvasBaseH;
const s = Math.min(sx, sy);
this.mapScale = s;
// this.mapScale = sx;
// this.mapScale = sy;
this.renderArr = [];
}
/**
* 初始化试图
*/
initView() {
this.initPic();
this.initBottomPart();
}
initBottomPart() {
const btnLeft = new MySprite();
btnLeft.init(this.images.get('btn_left'));
btnLeft.x = this.canvasWidth - 150 * this.mapScale;
btnLeft.y = this.canvasHeight - 100 * this.mapScale;
btnLeft.setScaleXY(this.mapScale);
this.renderArr.push(btnLeft);
this.btnLeft = btnLeft;
const btnRight = new MySprite();
btnRight.init(this.images.get('btn_right'));
btnRight.x = this.canvasWidth - 50 * this.mapScale;
btnRight.y = this.canvasHeight - 100 * this.mapScale;
btnRight.setScaleXY(this.mapScale);
this.renderArr.push(btnRight);
this.btnRight = btnRight;
}
initPic() { const vh = this.readingContainer.nativeElement.offsetHeight
const w = this.readingContainer.nativeElement.offsetWidth / 2 - 0.04 * vh;
const h = this.readingContainer.nativeElement.offsetHeight - 0.08 * vh;
const maxW = this.canvasWidth * 0.7; // const w_zoom = pos.hl_w / w;
// const h_zoom = pos.hl_h / h;
const pic1 = new MySprite(); // const zoom = Math.min(w_zoom, h_zoom);
pic1.init(this.images.get(this.data.pic_url)); const part = this.parts[index];
pic1.x = this.canvasWidth / 2; let zoom = w / part.data.width;
pic1.y = this.canvasHeight / 2; if (part.data.height >= part.data.width) {
pic1.setScaleXY(maxW / pic1.width); zoom = h / part.data.height;
this.renderArr.push(pic1);
this.pic1 = pic1;
const label1 = new Label();
label1.text = this.data.text;
label1.textAlign = 'center';
label1.fontSize = 50;
label1.fontName = 'BRLNSDB';
label1.fontColor = '#ffffff';
pic1.addChild(label1);
const pic2 = new MySprite();
pic2.init(this.images.get(this.data.pic_url_2));
pic2.x = this.canvasWidth / 2 + this.canvasWidth;
pic2.y = this.canvasHeight / 2;
pic2.setScaleXY(maxW / pic2.width);
this.renderArr.push(pic2);
this.pic2 = pic2;
this.curPic = pic1;
}
btnLeftClicked() {
this.lastPage();
}
btnRightClicked() {
this.nextPage();
}
lastPage() {
if (this.curPic == this.pic1) {
return;
} }
this.canTouch = false;
const moveLen = this.canvasWidth;
tweenChange(this.pic1, {x: this.pic1.x + moveLen}, 1);
tweenChange(this.pic2, {x: this.pic2.x + moveLen}, 1, () => {
this.canTouch = true;
this.curPic = this.pic1;
});
}
nextPage() {
if (this.curPic == this.pic2) { const p_w = Math.floor(this.imgData.naturalWidth * zoom);
return; const p_h = Math.floor(this.imgData.naturalHeight * zoom);
} const i_w = Math.floor(part.data.width * zoom);
const i_h = Math.floor(part.data.height * zoom);
const p_x = Math.floor(part.data.x * zoom);
const p_y = Math.floor(part.data.y * zoom);
this.canTouch = false;
const moveLen = this.canvasWidth; this.partViewDetailCss = {
tweenChange(this.pic1, {x: this.pic1.x - moveLen}, 1); 'width.px': i_w,
tweenChange(this.pic2, {x: this.pic2.x - moveLen}, 1, () => { 'height.px': i_h,
this.canTouch = true; 'border': `3px solid ${this.colors[index]}`
this.curPic = this.pic2; };
}); this.partViewImgDetailCss = {
} 'width.px': p_w,
'height.px': p_h,
'max-width.px': p_w,
'max-height.px': p_h,
'transform': `translateX(-${p_x}px) translateY(-${p_y}px)`
};
pic1Clicked() { this.partDetailVisible = true;
this.playAudio(this.data.audio_url);
} }
closeDetailView(evt) {
pic2Clicked() { // if (evt.currentTarget === evt.target) {
this.playAudio(this.data.audio_url_2); this.partDetailVisible = false;
// }
} }
ngAfterViewInit() {
// this.autoSizeText();
if (!this.data.contentObj.parts) {
mapDown(event) {
if (!this.canTouch) {
return;
}
if ( this.checkClickTarget(this.btnLeft) ) {
this.btnLeftClicked();
return;
}
if ( this.checkClickTarget(this.btnRight) ) {
this.btnRightClicked();
return;
}
if ( this.checkClickTarget(this.pic1) ) {
this.pic1Clicked();
return; return;
} }
setTimeout(() => {
if ( this.checkClickTarget(this.pic2) ) { let w = this.previewParts.nativeElement.offsetWidth / 2;
this.pic2Clicked(); const b = document.querySelector('body');
return; w = w - 30 - (b.clientHeight / 100);
} const h = w * 0.5625;
}
mapMove(event) { this.parts = this.data.contentObj.parts.map((item, idx) => {
const color = this.colors[idx];
item.color = color;
const nitem = Object.assign({}, item);
} const {iw, ih, ix, iy, pw, ph } = this.calcCssProp(nitem.data, w, h);
nitem.previewCss = {'width.px': pw, 'height.px': ph };
nitem.imgCss = {
'width.px': iw,
'height.px': ih,
transform: `translateX(-${ix}px) translateY(-${iy}px)`
};
mapUp(event) { return nitem;
}, 1);
// setTimeout(() => {
// this.autoSizeText();
// }, 10);
this.cw = this.previewContainer.nativeElement.offsetWidth;
this.ch = this.previewContainer.nativeElement.offsetHeight;
});
} }
update() {
// ----------------------------------------------------------
this.animationId = window.requestAnimationFrame(this.update.bind(this));
// 清除画布内容
this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
// tween 更新动画
TWEEN.update();
// ----------------------------------------------------------
this.updateArr(this.renderArr);
}
} }
@mixin hide-overflow-text {
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
@mixin k-no-select {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
@mixin k-img-bg {
background-repeat: no-repeat;
background-position: center;
background-size: contain;
}
/* You can add global styles to this file, and also import other style files */
/* You can add global styles to this file, and also import other style files */
/* You can add global styles to this file, and also import other style files */
/* You can add global styles to this file, and also import other style files */
html, body {
width: 100%;
height: 100%;
font-size: 16px;
}
body {
background-color: #f0f2f5 !important;
}
* {
outline: none;
}
@font-face {
font-family: ChalkboardSE-Regular;
src: url('/assets/font/ChalkboardSE-Regular.woff') format('WOFF');
}
.k-16-9 {
position: relative;
width: 100%;
height: fit-content;
&:before {
content: "";
display: block;
position: relative;
width: 100%;
height: 0;
padding-bottom: 56.25%;
}
}
.k-full-fill {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
}
.k-flex-center {
display: flex;
align-items: center;
justify-content: center;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment