diff --git a/angular.json b/angular.json
index 5952480a9ced5a16dc48a17e46fb1669d4e89426..c46936b10f55fdfb9c9f7319765c57bef8d4eb3d 100644
--- a/angular.json
+++ b/angular.json
@@ -30,7 +30,10 @@
             ],
             "styles": [
               "./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": []
           },
diff --git a/package.json b/package.json
index c9142eda9628c3fc9cf5db44b62bc836760bdad5..7305e6006f4c94a3087b04167971f9ca74ccc9b0 100644
--- a/package.json
+++ b/package.json
@@ -26,7 +26,11 @@
     "@fortawesome/free-solid-svg-icons": "^5.12.1",
     "@tweenjs/tween.js": "~18.5.0",
     "ali-oss": "^6.5.1",
+    "animate.css": "3.7.0",
+    "bootstrap": "4.1.1",
     "compressing": "^1.5.0",
+    "font-awesome": "4.7.0",
+    "lodash": "4.17.10",
     "ng-zorro-antd": "^8.5.2",
     "rxjs": "~6.5.4",
     "spark-md5": "^3.0.0",
diff --git a/src/app/form/form.component.css b/src/app/form/form.component.css
deleted file mode 100644
index 41f7757290161973406aeec2177e0feef3ffcdaa..0000000000000000000000000000000000000000
--- a/src/app/form/form.component.css
+++ /dev/null
@@ -1,8 +0,0 @@
-@import '../style/common_mixin.css';
-
-
-.model-content {
-  width: 100%;
-  height: 100%;
-
-}
diff --git a/src/app/form/form.component.html b/src/app/form/form.component.html
index a51aa6ba14036cd5e1b9a4544b55a7bf0b75c1c8..a17f2640cd1b43996951340ddf0386d2ecaa1159 100644
--- a/src/app/form/form.component.html
+++ b/src/app/form/form.component.html
@@ -1,25 +1,91 @@
-
-<div class="model-content">
-
-
-  <div style="position: absolute; left: 200px; top: 100px; width: 800px;">
-
-    <input type="text" nz-input [(ngModel)]="item.text" (blur)="save()">
-
+<nz-form-item>
+  <nz-form-label [nzSpan]="4">Vocabulary</nz-form-label>
+  <nz-form-control [nzSpan]="8">
     <app-upload-image-with-preview
-      [picUrl]="item.pic_url"
-      (imageUploaded)="onImageUploadSuccess($event, 'pic_url')"
+      [picUrl]="item.contentObj.pic_id"
+      (imageUploaded)="onHandleQuestionImage($event)"
     ></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>
- 
\ No newline at end of file
+    <input type="text" nz-input placeholder="Please input title"
+           [(ngModel)]="item.contentObj.title" (blur)="updateQuestionTitle()">
+  </nz-form-control>
+  <app-audio-recorder [audioUrl]="item.contentObj.question_audio_id"
+                      (audioUploaded)="onHandleQuestionOptionItemAudio($event)">
+  </app-audio-recorder>
+</nz-form-item>
+<nz-form-item>
+  <nz-form-label [nzSpan]="4">Option</nz-form-label>
+  <nz-form-control [nzSpan]="20">
+    <button
+      nz-button
+      nzType="default"
+      (click)="addNewOptionItem()"
+      [disabled]="options.length >= 4"
+    >
+      <i class="anticon anticon-plus-circle-o"></i>
+      Add
+    </button>
+    <div class="opt-container">
+      <div
+        class="opt-wrap"
+        *ngFor="let item of options; let i = index"
+      >
+        <div class="tool-bar">
+          <label nz-checkbox [ngModel]="item.right"
+                 (ngModelChange)="setRightAnswer(i)" class="right-answer">
+            Right
+          </label>
+          <div class="button-group">
+            <button
+              nz-button
+              nzType="primary"
+              nzSize="small"
+              nzShape="circle"
+              style="margin-right: 1rem;"
+              (click)="onHandleSwitchContent(item)"
+            >
+              <i class="anticon anticon-retweet"></i>
+            </button>
+            <nz-dropdown [nzTrigger]="'click'">
+              <button
+                nz-button
+                nzType="primary"
+                nzSize="small"
+                nzShape="circle"
+                nz-dropdown
+              >
+                <i class="anticon anticon-setting"></i>
+              </button>
+              <ul nz-menu>
+                <li nz-menu-item (click)="onMovePage(i, -1)" [class.disabled]="i < 1">
+                  <i class="anticon anticon-arrow-up"></i>
+                  Move Up
+                </li>
+                <li nz-menu-item (click)="onMovePage(i, 1)" [class.disabled]="i + 1 >= options.length">
+                  <i class="anticon anticon-arrow-down"></i>
+                  Move Down
+                </li>
+                <li nz-menu-item (click)="onDeleteCoursewareItem(i)">
+                  <i class="anticon anticon-delete"></i>
+                  Delete
+                </li>
+              </ul>
+            </nz-dropdown>
+          </div>
+        </div>
+        <div class="content-editor">
+          <input *ngIf="item.type === OPTION_TYPE.TEXT" type="text" nz-input [(ngModel)]="item.text"
+                 placeholder="Please input title" (blur)="updateOptionItemText(i)">
+          <app-upload-image-with-preview
+            *ngIf="item.type === OPTION_TYPE.PIC"
+            [picUrl]="item.pic_id"
+            (imageUploaded)="onHandleOptionItemImage($event, i)"
+          ></app-upload-image-with-preview>
+          <app-audio-recorder *ngIf="item.type === OPTION_TYPE.AUDIO"
+                              [audioUrl]="item.audio_id"
+                              (audioUploaded)="onHandleOptionItemAudio($event, i)">
+          </app-audio-recorder>
+        </div>
+      </div>
+    </div>
+  </nz-form-control>
+</nz-form-item>
diff --git a/src/app/form/form.component.scss b/src/app/form/form.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..ba5a8d5ae5fc59cb2f2ff8c28d79b435f4924509
--- /dev/null
+++ b/src/app/form/form.component.scss
@@ -0,0 +1,28 @@
+.opt-container {
+  width: 100%;
+  display: flex;
+  flex-wrap: wrap;
+.opt-wrap {
+  margin: 0 1rem 1rem 0;
+  display: flex;
+  flex-direction: column;
+  height: 14rem;
+  width: 20rem;
+  border: 2px dashed #ddd;
+  border-radius: 0.5rem;
+  padding: 0.8rem;
+.tool-bar {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+.content-editor {
+  height: 100%;
+}
+}
+}
+
+.disabled {
+  pointer-events: none;
+  opacity: 0.6;
+}
diff --git a/src/app/form/form.component.ts b/src/app/form/form.component.ts
index 8bf084e921171b4cf11f2779fd6dfc49daa67fdb..c9de59da7415d7fcc2d57de94d2a69bae9efa283 100644
--- a/src/app/form/form.component.ts
+++ b/src/app/form/form.component.ts
@@ -1,36 +1,53 @@
 import {Component, EventEmitter, Input, OnDestroy, OnChanges, OnInit, Output, ApplicationRef, ChangeDetectorRef} from '@angular/core';
-
+import * as _ from 'lodash';
+import { NzModalService } from 'ng-zorro-antd';
 
 
 @Component({
   selector: 'app-form',
   templateUrl: './form.component.html',
-  styleUrls: ['./form.component.css']
+  styleUrls: ['./form.component.scss']
 })
 export class FormComponent implements OnInit, OnChanges, OnDestroy {
 
   // 储存数据用
-  saveKey = "test_0011";
+  saveKey = 'ww_question2';
   // 储存对象
   item;
+  originContent;
+  options = [];
+  OPTION_TYPE = {TEXT: 1, PIC: 2, AUDIO: 3};
 
 
-  constructor(private appRef: ApplicationRef,private changeDetectorRef: ChangeDetectorRef) {
+  constructor(private appRef: ApplicationRef,
+              private modalService: NzModalService,
+              private changeDetectorRef: ChangeDetectorRef) {
 
   }
 
 
   ngOnInit() {
 
-    this.item = {};
+    this.item = {
+      contentObj: {}
+    };
 
     // 获取存储的数据
-    (<any> window).courseware.getData((data) => {
+    (window as any).courseware.getData((data) => {
 
       if (data) {
         this.item = data;
+        this.originContent = {...this.item.contentObj};
+        this.options = _.get(this.item.contentObj, 'options', []);
+        // Ken 2019-04-14 17:41 把showPic, showText的逻辑换为type
+        _.forEach(this.options, o => {
+          if (_.isUndefined(o.type)) {
+            o.type = o.showPic ? this.OPTION_TYPE.PIC : this.OPTION_TYPE.TEXT;
+          }
+        });
       }
 
+
       this.init();
       this.changeDetectorRef.markForCheck();
       this.changeDetectorRef.detectChanges();
@@ -54,25 +71,18 @@ export class FormComponent implements OnInit, OnChanges, OnDestroy {
   }
 
 
-  /**
-   * 储存图片数据
-   * @param e
-   */
-  onImageUploadSuccess(e, key) {
-
-      this.item[key] = e.url;
-      this.save();
-  }
+  // onImageUploadSuccess(e, key) {
+  //
+  //     this.item[key] = e.url;
+  //     this.save();
+  // }
 
-  /**
-   * 储存音频数据
-   * @param e
-   */
-  onAudioUploadSuccess(e, key) {
 
-    this.item[key] = e.url;
-    this.save();
-  }
+  // onAudioUploadSuccess(e, key) {
+  //
+  //   this.item[key] = e.url;
+  //   this.save();
+  // }
 
 
 
@@ -80,7 +90,7 @@ export class FormComponent implements OnInit, OnChanges, OnDestroy {
    * 储存数据
    */
   save() {
-    (<any> window).courseware.setData(this.item, null, this.saveKey);
+    (window as any).courseware.setData(this.item, null, this.saveKey);
     this.refresh();
   }
 
@@ -93,5 +103,90 @@ export class FormComponent implements OnInit, OnChanges, OnDestroy {
     }, 1);
   }
 
+
+  onHandleQuestionImage(e) {
+    this.item.contentObj.pic_id = e.url;
+    this.save();
+  }
+
+  updateQuestionTitle() {
+    this.save();
+  }
+
+  onHandleOptionItemImage(e, index) {
+    this.options[index].pic_id = e.url;
+    this.item.contentObj.options = this.options;
+    this.save();
+  }
+
+  onHandleOptionItemAudio(e, index) {
+    this.options[index].audio_id = e.url;
+    this.item.contentObj.options = this.options;
+    this.save();
+  }
+  onHandleQuestionOptionItemAudio(e) {
+    console.log(e);
+    this.item.contentObj.question_audio_id = e.url;
+    this.save();
+  }
+  updateOptionItemText(index) {
+    this.item.contentObj.options = this.options;
+    this.save();
+  }
+
+  setRightAnswer(index) {
+    this.options[index].right = !this.options[index].right;
+    this.item.contentObj.options = this.options;
+    this.save();
+  }
+
+  addNewOptionItem() {
+    const content = {
+      text: '',
+      pic_id: null,
+      right: false,
+      type: this.OPTION_TYPE.TEXT,
+    };
+    this.options.push(content);
+  }
+
+  onHandleSwitchContent(item) {
+    switch (item.type) {
+      case this.OPTION_TYPE.TEXT:
+        item.type = this.OPTION_TYPE.PIC;
+        break;
+      case this.OPTION_TYPE.PIC:
+        item.type = this.OPTION_TYPE.AUDIO;
+        break;
+      case this.OPTION_TYPE.AUDIO:
+        item.type = this.OPTION_TYPE.TEXT;
+        break;
+    }
+    this.item.contentObj.options = this.options;
+    this.save();
+  }
+  onMovePage(index, direction) {
+    const temp = this.options[index + direction];
+    this.options[index + direction] = this.options[index];
+    this.options[index] = temp;
+    this.options = [...this.options];
+    this.item.contentObj.options = this.options;
+    this.save();
+  }
+
+  onDeleteCoursewareItem(index) {
+    this.modalService.confirm({
+      nzTitle: 'Are you sure delete this?',
+      nzOkText: 'Yes',
+      nzOkType: 'danger',
+      nzCancelText: 'No',
+      nzOnOk: () => {
+        this.options.splice(index, 1);
+        this.item.contentObj.options = this.options;
+        this.save();
+      },
+    });
+  }
+
 }
 
diff --git a/src/app/play/play.component.css b/src/app/play/play.component.css
deleted file mode 100644
index bbda49566c0a6aa2c621e2f863caf1849e492572..0000000000000000000000000000000000000000
--- a/src/app/play/play.component.css
+++ /dev/null
@@ -1,19 +0,0 @@
-.game-container {
-  width: 100%;
-  height: 100%;
-  background: #ffffff;
-  background-size: cover;
-}
-
-#canvas {
-
-}
-
-
-
-@font-face
-{
-  font-family: 'BRLNSDB';
-  src: url("../../assets/font/BRLNSDB.TTF") ;
-}
-
diff --git a/src/app/play/play.component.html b/src/app/play/play.component.html
index 960fb495a359c3c7803c4370d8c7d8457517d70e..cd2d681da77331d651e2efa5794c7911175ce45f 100644
--- a/src/app/play/play.component.html
+++ b/src/app/play/play.component.html
@@ -1,3 +1,36 @@
-<div class="game-container" #wrap>
-  <canvas id="canvas" #canvas></canvas>
+<div class="question-player" style="position: relative">
+  <div class="top-part">
+    <img
+      *ngIf="data.contentObj.pic_id"
+      [src]="data.contentObj.pic_id "
+      alt="cover"
+      (dragstart)="false;$event.preventDefault();"
+      (click)="onHandleZoomIn($event)"
+      [@zoom]="zoomIn ? 'in' : 'none'"
+    />
+    <div class="title">{{data.contentObj.title}}</div>
+  </div>
+  <div class="opt-container">
+    <div
+      class="opt-wrap"
+      *ngFor="let item of options; let i = index"
+      [@tip-border]="item.state || 'none'"
+      (click)="handleAnimation(item)"
+    >
+      <span class="opt-circle" [@tip-circle]="item.state || 'none'">{{transCharCode(i)}}</span>
+      <div class="text" *ngIf="item.type === OPTION_TYPE.TEXT">{{item.text}}</div>
+      <div class="audio" style="text-align: center" *ngIf="item.type === OPTION_TYPE.AUDIO" (click)="playAudio(item, $event)">
+        <i class="anticon anticon-sound"></i>
+      </div>
+      <img *ngIf="item.type === OPTION_TYPE.PIC" [src]="item.pic_id" alt=""
+           (dragstart)="false;$event.preventDefault();"/>
+    </div>
+  </div>
+  <div (click)="playQuestionAudio($event)"
+       style="width: 100px;height: 100px;position: absolute;bottom: 50px;right: 80px;">
+    <img style="max-width: 100%;max-height: 100%;" *ngIf="!playerState.getValue().isPlaying" src="/assets/question/play.png" alt="" (dragstart)="false;$event.preventDefault();">
+    <img style="max-width: 100%;max-height: 100%;" *ngIf="playerState.getValue().isPlaying" src="/assets/question/pause.png" alt="" (dragstart)="false;$event.preventDefault();">
+  </div>
+  <!--<img src="/assets/question/left.png" alt="ufo" class="left-ufo" (dragstart)="false;$event.preventDefault();">-->
+  <!--<img src="/assets/question/right.png" alt="ufo" class="right-ufo" (dragstart)="false;$event.preventDefault();">-->
 </div>
diff --git a/src/app/play/play.component.scss b/src/app/play/play.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..00b6669c9cba487364bb7eaa820ec1b8156ef370
--- /dev/null
+++ b/src/app/play/play.component.scss
@@ -0,0 +1,103 @@
+.question-player {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  height: 100%;
+  width: 100%;
+  background-image: url("../../assets/common_bg.jpg");
+  background-size: cover;
+  background-repeat: no-repeat;
+
+  .top-part {
+    height: 35%;
+    min-width: 60%;
+    max-width: 90%;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+
+    img {
+      //width: 40%;
+      height: 60%;
+      margin-right: 1.5rem;
+      object-fit: contain;
+    }
+    .title {
+      font-size: 2.5rem;
+      color: #ffb00f;
+      background-color: white;
+      padding: 0.5rem 1rem;
+      border-radius: 1rem;
+    }
+  }
+  .opt-container {
+    height: 55%;
+    width: 80%;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    flex-wrap: wrap;
+
+    .opt-wrap {
+      width: 30%;
+      height: 45%;
+      position: relative;
+      background-color: #ffffff;
+      margin: 1rem;
+      border-radius: 0.8rem;
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      border: 0.3rem solid #ffffff;
+
+      .opt-circle {
+        width: 2.5rem;
+        height: 2.5rem;
+        background-color: #ffffff;
+        position: absolute;
+        border-radius: 50%;
+        left: -1rem;
+        top: -1rem;
+        text-align: center;
+        line-height: 2.5rem;
+        font-size: 24px;
+        font-weight: 500;
+      }
+
+      img {
+        width: 100%;
+        height: 100%;
+        object-fit: contain;
+      }
+
+      .text {
+        font-size: 28px;
+        font-weight: 500;
+      }
+
+      .audio {
+        color: white;
+        background-color: #00BCD4;
+        border-radius: 5px;
+        font-size: 5vh;
+        font-weight: 500;
+        width: 13vh;
+        cursor: pointer;
+      }
+    }
+  }
+  .left-ufo {
+    width: 10%;
+    object-fit: contain;
+    position: absolute;
+    left: 15vh;
+    bottom: 20vh;
+  }
+  .right-ufo {
+    width: 10%;
+    object-fit: contain;
+    position: absolute;
+    right: 15vh;
+    bottom: 20vh;
+  }
+}
diff --git a/src/app/play/play.component.ts b/src/app/play/play.component.ts
index f4b8a863d45aa9f51852483802422d4dfacce939..b6ed09ef238fbe89692c0904b8dc808396e25e27 100644
--- a/src/app/play/play.component.ts
+++ b/src/app/play/play.component.ts
@@ -1,5 +1,5 @@
 import {Component, ElementRef, ViewChild, OnInit, Input, OnDestroy, HostListener} from '@angular/core';
-
+import * as _ from 'lodash';
 import {
   Label,
   MySprite, tweenChange,
@@ -11,6 +11,8 @@ import {Subject} from 'rxjs';
 import {debounceTime} from 'rxjs/operators';
 
 import TWEEN from '@tweenjs/tween.js';
+import {BehaviorSubject} from 'rxjs';
+import { animate, keyframes, state, style, transition, trigger } from '@angular/animations';
 
 
 
@@ -18,662 +20,187 @@ import TWEEN from '@tweenjs/tween.js';
 @Component({
   selector: 'app-play',
   templateUrl: './play.component.html',
-  styleUrls: ['./play.component.css']
+  styleUrls: ['./play.component.scss'],
+  animations: [
+    trigger('tip-border', [
+      state('right', style({border: '0.3rem solid #52E55D', 'background-color': '#52E55D', transform: 'scale(1)'})),
+      state('none', style({transform: 'scale(1)'})),
+      state('wrong', style({border: '0.3rem solid #ff3333', 'background-color': '#ff3333', transform: 'scale(1)'})),
+      transition('none => wrong', animate('.2s ease-in', keyframes([
+        style({transform: 'scale(1)'}),
+        style({transform: 'scale(0.6)'}),
+        style({transform: 'scale(1)'}),
+      ]))), // 第一个参数:动画时间, 第二个参数:动画延迟时间
+      transition('none => right', animate('.2s ease-in', keyframes([
+        style({transform: 'scale(1)'}),
+        style({transform: 'scale(0.6)'}),
+        style({transform: 'scale(1)'}),
+      ]))),
+      transition('wrong => none', animate('.2s ease-out'))
+    ]),
+    trigger('tip-circle', [
+      state('right', style({'background-color': '#52E55D'})),
+      state('none', style({})),
+      state('wrong', style({'background-color': '#ff3333'})),
+      transition('none => wrong', animate('.2s ease-in')), // 第一个参数:动画时间, 第二个参数:动画延迟时间
+      transition('none => right', animate('.2s ease-in')),
+      transition('wrong => none', animate('.2s ease-out'))
+    ]),
+    trigger('zoom', [
+      state('in', style({
+        position: 'absolute', 'z-index': '1', top: '0', left: '0', right: '0', bottom: '0', margin: 'auto',
+        height: '60%', 'object-fit': 'contain', 'max-width': '80%'
+      })),
+      state('none', style({})),
+      transition('none => in', animate('.3s', keyframes([
+        style({
+          height: '10%', position: 'absolute', 'z-index': '1', top: '0', left: '0',
+          right: '0', bottom: '0', margin: 'auto', 'object-fit': 'contain', 'max-width': '80%',
+          opacity: '0'
+        }),
+        style({
+          height: '20%', position: 'absolute', 'z-index': '1', top: '0', left: '0',
+          right: '0', bottom: '0', margin: 'auto', 'object-fit': 'contain', 'max-width': '80%',
+          opacity: '0.2'
+        }),
+        style({
+          height: '30%', position: 'absolute', 'z-index': '1', top: '0', left: '0',
+          right: '0', bottom: '0', margin: 'auto', 'object-fit': 'contain', 'max-width': '80%',
+          opacity: '0.4'
+        }),
+        style({
+          height: '40%', position: 'absolute', 'z-index': '1', top: '0', left: '0',
+          right: '0', bottom: '0', margin: 'auto', 'object-fit': 'contain', 'max-width': '80%',
+          opacity: '0.6'
+        }),
+        style({
+          height: '50%', position: 'absolute', 'z-index': '1', top: '0', left: '0',
+          right: '0', bottom: '0', margin: 'auto', 'object-fit': 'contain', 'max-width': '80%',
+          opacity: '0.8'
+        }),
+      ]))), // 第一个参数:动画时间, 第二个参数:动画延迟时间
+      transition('in => none', animate('.5s ease-out'))
+    ])
+  ]
 })
 export class PlayComponent implements OnInit, OnDestroy {
-
-  @ViewChild('canvas', {static: true }) canvas: ElementRef;
-  @ViewChild('wrap', {static: true }) wrap: ElementRef;
+  saveKey = 'ww_question2';
+  questionAudio = new Audio();
+  playerState = new BehaviorSubject<any>({ isPlaying: false });
+  options;
+  zoomIn;
+  rightAudio = new Audio();
+  wrongAudio = new Audio();
+  OPTION_TYPE = {TEXT: 1, PIC: 2, AUDIO: 3};
+  optionAudio = new Audio();
 
   // 数据
   data;
+  constructor() {
+    this.rightAudio.src = '/assets/right.mp3';
+    this.rightAudio.load();
+    this.wrongAudio.src = '/assets/wrong.mp3';
+    this.wrongAudio.load();
+    this.questionAudio.addEventListener('play', () => {
+      this.playerState.next({ isPlaying: true });
+    });
 
-  ctx;
-
-  canvasWidth = 1280; // canvas实际宽度
-  canvasHeight = 720; // canvas实际高度
-
-  canvasBaseW = 1280; // canvas 资源预设宽度
-  canvasBaseH = 720;  // canvas 资源预设高度
-
-  mx; // 点击x坐标
-  my; // 点击y坐标
-
-
-  // 资源
-  rawImages = new Map(res);
-  rawAudios = new Map(resAudio);
-
-  images = new Map();
-
-  animationId: any;
-  winResizeEventStream = new Subject();
-
-  audioObj = {};
-
-  renderArr;
-  mapScale = 1;
-
-  canvasLeft;
-  canvasTop;
-
-  saveKey = 'test_0011';
-
-
-  btnLeft;
-  btnRight;
-  pic1;
-  pic2;
-
-  canTouch = true;
-
-  curPic;
-
-  @HostListener('window:resize', ['$event'])
-  onResize(event) {
-    this.winResizeEventStream.next();
+    this.questionAudio.addEventListener('pause', () => {
+      this.playerState.next({ isPlaying: false });
+    });
   }
 
 
-  ngOnInit() {
 
-    this.data = {};
+  ngOnInit() {
 
+    this.data = {
+      contentObj: {}
+    };
+    this.zoomIn = false;
+    this.options = _.get(this.data.contentObj, 'options', []).map(item => {
+      item.state = '';
+      // Ken 2019-04-15 22:38  把showPic, showText的逻辑换为type
+      if (_.isUndefined(item.type)) {
+        item.type = item.showPic ? this.OPTION_TYPE.PIC : this.OPTION_TYPE.TEXT;
+      }
+      return item;
+    });
     // 获取数据
-    const getData = (<any> window).courseware.getData;
+    const getData = (window as any).courseware.getData;
     getData((data) => {
 
-      if (data && typeof data == 'object') {
+      if (data && typeof data === 'object') {
+        console.log(data);
         this.data = data;
+        this.zoomIn = false;
+        this.options = _.get(this.data.contentObj, 'options', []).map(item => {
+          item.state = '';
+          // Ken 2019-04-15 22:38  把showPic, showText的逻辑换为type
+          if (_.isUndefined(item.type)) {
+            item.type = item.showPic ? this.OPTION_TYPE.PIC : this.OPTION_TYPE.TEXT;
+          }
+          return item;
+        });
+      }
+      const question_audio_id = _.get(this.data.contentObj, 'question_audio_id', null);
+      // console.log('question_audio_id', question_audio_id);
+      this.questionAudio.src = question_audio_id;
+      this.questionAudio.load();
+      if (window['air']) {
+        window['air'].hideAirClassLoading(this.saveKey, this.data);
       }
-      // console.log('data:' , data);
-
-      // 初始化 各事件监听
-      this.initListener();
-
-      // 若无数据 则为预览模式 需要填充一些默认数据用来显示
-      this.initDefaultData();
-
-      // 初始化 音频资源
-      this.initAudio();
-      // 初始化 图片资源
-      this.initImg();
-      // 开始预加载资源
-      this.load();
-
     }, this.saveKey);
 
   }
 
   ngOnDestroy() {
-    window['curCtx'] = null;
-    window.cancelAnimationFrame(this.animationId);
-  }
-
-
-  load() {
-
-    // 预加载资源
-    this.loadResources().then(() => {
-      window["air"].hideAirClassLoading(this.saveKey, this.data);
-      this.init();
-      this.update();
-    });
+    this.rightAudio.pause();
+    this.wrongAudio.pause();
+    this.optionAudio.pause();
+    this.rightAudio = null;
+    this.wrongAudio = null;
+    this.optionAudio = null;
   }
 
-
-  init() {
-
-    this.initCtx();
-    this.initData();
-    this.initView();
-  }
-
-  initCtx() {
-    this.canvasWidth = this.wrap.nativeElement.clientWidth;
-    this.canvasHeight = this.wrap.nativeElement.clientHeight;
-    this.canvas.nativeElement.width = this.wrap.nativeElement.clientWidth;
-    this.canvas.nativeElement.height = this.wrap.nativeElement.clientHeight;
-
-
-    this.ctx = this.canvas.nativeElement.getContext('2d');
-    this.canvas.nativeElement.width = this.canvasWidth;
-    this.canvas.nativeElement.height = this.canvasHeight;
-
-    window['curCtx'] = this.ctx;
-  }
-
-
-
-
-
-
-  updateItem(item) {
-    if (item) {
-      item.update();
-    }
-  }
-
-  updateArr(arr) {
-    if (!arr) {
-      return;
-    }
-    for (let i = 0; i < arr.length; i++) {
-      arr[i].update(this);
-    }
-  }
-
-
-
-
-
-
-
-  initListener() {
-
-    this.winResizeEventStream
-      .pipe(debounceTime(500))
-      .subscribe(data => {
-        this.renderAfterResize();
-      });
-
-
-    // ---------------------------------------------
-    const setParentOffset = () => {
-      const rect = this.canvas.nativeElement.getBoundingClientRect();
-      this.canvasLeft = rect.left;
-      this.canvasTop = rect.top;
-    };
-    const setMxMyByTouch = (event) => {
-      if (event.touches.length <= 0) {
-        return;
-      }
-      if (this.canvasLeft == null) {
-        setParentOffset();
-      }
-      this.mx = event.touches[0].pageX - this.canvasLeft;
-      this.my = event.touches[0].pageY - this.canvasTop;
-    };
-
-    const setMxMyByMouse = (event) => {
-      this.mx = event.offsetX;
-      this.my = event.offsetY;
-    };
-    // ---------------------------------------------
-
-
-    let firstTouch = true;
-
-    const touchDownFunc = (e) => {
-      if (firstTouch) {
-        firstTouch = false;
-        removeMouseListener();
-      }
-      setMxMyByTouch(e);
-      this.mapDown(e);
-    };
-    const touchMoveFunc = (e) => {
-      setMxMyByTouch(e);
-      this.mapMove(e);
-    };
-    const touchUpFunc = (e) => {
-      setMxMyByTouch(e);
-      this.mapUp(e);
-    };
-
-    const mouseDownFunc = (e) => {
-      if (firstTouch) {
-        firstTouch = false;
-        removeTouchListener();
-      }
-      setMxMyByMouse(e);
-      this.mapDown(e);
-    };
-    const mouseMoveFunc = (e) => {
-      setMxMyByMouse(e);
-      this.mapMove(e);
-    };
-    const mouseUpFunc = (e) => {
-      setMxMyByMouse(e);
-      this.mapUp(e);
-    };
-
-
-    const element = this.canvas.nativeElement;
-
-    const addTouchListener = () => {
-      element.addEventListener('touchstart', touchDownFunc);
-      element.addEventListener('touchmove', touchMoveFunc);
-      element.addEventListener('touchend', touchUpFunc);
-      element.addEventListener('touchcancel', touchUpFunc);
-    };
-    const removeTouchListener = () => {
-      element.removeEventListener('touchstart', touchDownFunc);
-      element.removeEventListener('touchmove', touchMoveFunc);
-      element.removeEventListener('touchend', touchUpFunc);
-      element.removeEventListener('touchcancel', touchUpFunc);
-    };
-
-    const addMouseListener = () => {
-      element.addEventListener('mousedown', mouseDownFunc);
-      element.addEventListener('mousemove', mouseMoveFunc);
-      element.addEventListener('mouseup', mouseUpFunc);
-    };
-    const removeMouseListener = () => {
-      element.removeEventListener('mousedown', mouseDownFunc);
-      element.removeEventListener('mousemove', mouseMoveFunc);
-      element.removeEventListener('mouseup', mouseUpFunc);
-    };
-
-    addMouseListener();
-    addTouchListener();
+  transCharCode(index) {
+    return String.fromCharCode(65 + index);
   }
 
-
-  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();
+  handleAnimation(item) {
+    if (item.right) {
+      item.state = 'right';
+      this.rightAudio.play();
+    } else {
+      item.state = 'wrong';
+      this.wrongAudio.play();
+      setTimeout(() => {
+        item.state = '';
+      }, 1500);
     }
   }
 
-
-
-  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;
-    });
+  onHandleZoomIn(e) {
+    e.stopPropagation();
+    this.zoomIn = !this.zoomIn;
   }
 
-  preloadAudio(url) {
-    return new Promise((resolve, reject) => {
-      const audio = new Audio();
-      audio.oncanplay = (a) => {
-        resolve();
-      };
-      audio.onerror = () => {
-        reject();
-      };
-      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;
+  playAudio(option, e) {
+    e.stopPropagation();
+    this.optionAudio.src = option.audio_id;
+    // this.optionAudio.load();
+    this.optionAudio.play();
   }
-
-  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;
+  currentQuestionStatus() {
+    const sts = this.playerState.asObservable();
+    console.log(this.playerState.getValue().isPlaying)
   }
-
-  checkPointInRect(x, y, rect) {
-    if (x >= rect.x && x <= rect.x + rect.width) {
-      if (y >= rect.y && y <= rect.y + rect.height) {
-        return true;
-      }
+  playQuestionAudio(e) {
+    e.stopPropagation();
+    if (this.questionAudio.paused) {
+      this.questionAudio.play();
+    } else {
+      this.questionAudio.pause();
     }
-    return false;
   }
-
-
-
-
-
-  addUrlToAudioObj(key, url = null, vlomue = 1, loop = false, callback = null) {
-
-    const audioObj = this.audioObj;
-
-    if (url == null) {
-      url = key;
-    }
-
-    this.rawAudios.set(key, url);
-
-    const audio = new Audio();
-    audio.src = url;
-    audio.load();
-    audio.loop = loop;
-    audio.volume = vlomue;
-
-    audioObj[key] = audio;
-
-    if (callback) {
-      audio.onended = () => {
-        callback();
-      };
-    }
-  }
-
-  addUrlToImages(url) {
-    this.rawImages.set(url, url);
-  }
-
-
-
-
-
-
-  // ======================================================编写区域==========================================================================
-
-
-
-
-
-  /**
-   * 添加默认数据 便于无数据时的展示
-   */
-  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';
-    }
-  }
-
-
-  /**
-   * 添加预加载图片
-   */
-  initImg() {
-
-    this.addUrlToImages(this.data.pic_url);
-    this.addUrlToImages(this.data.pic_url_2);
-
-  }
-
-  /**
-   * 添加预加载音频
-   */
-  initAudio() {
-
-    // 音频资源
-    this.addUrlToAudioObj(this.data.audio_url);
-    this.addUrlToAudioObj(this.data.audio_url_2);
-
-    // 音效
-    this.addUrlToAudioObj('click', this.rawAudios.get('click'), 0.3);
-
-  }
-
-
-
-  /**
-   * 初始化数据
-   */
-  initData() {
-
-    const sx = this.canvasWidth / this.canvasBaseW;
-    const sy = this.canvasHeight / this.canvasBaseH;
-    const s = Math.min(sx, sy);
-    this.mapScale = s;
-
-    // this.mapScale = sx;
-    // this.mapScale = sy;
-
-
-    this.renderArr = [];
-
-
-
-  }
-
-
-
-  /**
-   * 初始化试图
-   */
-  initView() {
-
-
-    this.initPic();
-
-    this.initBottomPart();
-
-  }
-
-  initBottomPart() {
-
-    const btnLeft = new MySprite();
-    btnLeft.init(this.images.get('btn_left'));
-    btnLeft.x = this.canvasWidth - 150 * this.mapScale;
-    btnLeft.y = this.canvasHeight - 100 * this.mapScale;
-
-    btnLeft.setScaleXY(this.mapScale);
-
-    this.renderArr.push(btnLeft);
-
-    this.btnLeft = btnLeft;
-
-
-
-    const btnRight = new MySprite();
-    btnRight.init(this.images.get('btn_right'));
-    btnRight.x = this.canvasWidth - 50 * this.mapScale;
-    btnRight.y = this.canvasHeight - 100 * this.mapScale;
-    btnRight.setScaleXY(this.mapScale);
-
-    this.renderArr.push(btnRight);
-
-    this.btnRight = btnRight;
-  }
-
-  initPic() {
-
-    const maxW = this.canvasWidth * 0.7;
-
-    const pic1 = new MySprite();
-    pic1.init(this.images.get(this.data.pic_url));
-    pic1.x = this.canvasWidth / 2;
-    pic1.y = this.canvasHeight / 2;
-    pic1.setScaleXY(maxW / pic1.width);
-
-    this.renderArr.push(pic1);
-    this.pic1 = pic1;
-
-
-    const label1 = new Label();
-    label1.text = this.data.text;
-    label1.textAlign = 'center';
-    label1.fontSize = 50;
-    label1.fontName = 'BRLNSDB';
-    label1.fontColor = '#ffffff';
-
-    pic1.addChild(label1);
-
-
-
-
-
-    const pic2 = new MySprite();
-    pic2.init(this.images.get(this.data.pic_url_2));
-    pic2.x = this.canvasWidth / 2 + this.canvasWidth;
-    pic2.y = this.canvasHeight / 2;
-    pic2.setScaleXY(maxW / pic2.width);
-
-    this.renderArr.push(pic2);
-    this.pic2 = pic2;
-
-    this.curPic = pic1;
-  }
-
-
-  btnLeftClicked() {
-
-    this.lastPage();
-  }
-
-  btnRightClicked() {
-
-    this.nextPage();
-  }
-
-  lastPage() {
-
-    if (this.curPic == this.pic1) {
-      return;
-    }
-
-    this.canTouch = false;
-
-    const moveLen = this.canvasWidth;
-    tweenChange(this.pic1, {x: this.pic1.x + moveLen}, 1);
-    tweenChange(this.pic2, {x: this.pic2.x + moveLen}, 1, () => {
-      this.canTouch = true;
-      this.curPic = this.pic1;
-    });
-  }
-
-  nextPage() {
-
-    if (this.curPic == this.pic2) {
-      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.pic2;
-    });
-  }
-
-  pic1Clicked() {
-    this.playAudio(this.data.audio_url);
-  }
-
-  pic2Clicked() {
-    this.playAudio(this.data.audio_url_2);
-  }
-
-
-
-
-
-  mapDown(event) {
-
-    if (!this.canTouch) {
-      return;
-    }
-
-    if ( this.checkClickTarget(this.btnLeft) ) {
-      this.btnLeftClicked();
-      return;
-    }
-
-    if ( this.checkClickTarget(this.btnRight) ) {
-      this.btnRightClicked();
-      return;
-    }
-
-    if ( this.checkClickTarget(this.pic1) ) {
-      this.pic1Clicked();
-      return;
-    }
-
-    if ( this.checkClickTarget(this.pic2) ) {
-      this.pic2Clicked();
-      return;
-    }
-
-  }
-
-  mapMove(event) {
-
-  }
-
-  mapUp(event) {
-
-  }
-
-
-
-  update() {
-
-    // ----------------------------------------------------------
-    this.animationId = window.requestAnimationFrame(this.update.bind(this));
-    // 清除画布内容
-    this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
-    // tween 更新动画
-    TWEEN.update();
-    // ----------------------------------------------------------
-
-
-
-    this.updateArr(this.renderArr);
-
-
-  }
-
-
-
 }
diff --git a/src/app/style/mixin.scss b/src/app/style/mixin.scss
new file mode 100644
index 0000000000000000000000000000000000000000..dd32d36e1154249b2b4c2ec319ef10923ad39134
--- /dev/null
+++ b/src/app/style/mixin.scss
@@ -0,0 +1,18 @@
+@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;
+}
diff --git a/src/assets/common_bg.jpg b/src/assets/common_bg.jpg
new file mode 100755
index 0000000000000000000000000000000000000000..e1a3aac22a633ca64eb9bc038d0be799e5fda585
Binary files /dev/null and b/src/assets/common_bg.jpg differ
diff --git a/src/assets/question/bg.png b/src/assets/question/bg.png
new file mode 100644
index 0000000000000000000000000000000000000000..017779dcf41100c9bf31b1866b8c7d64d0452c63
Binary files /dev/null and b/src/assets/question/bg.png differ
diff --git a/src/assets/question/left.png b/src/assets/question/left.png
new file mode 100644
index 0000000000000000000000000000000000000000..d3d4d417b89705e6484e93d9894f3ab22e2a11d0
Binary files /dev/null and b/src/assets/question/left.png differ
diff --git a/src/assets/question/pause.png b/src/assets/question/pause.png
new file mode 100755
index 0000000000000000000000000000000000000000..671bc71ba557eca518ece6e9ae8b3e338b15cd1f
Binary files /dev/null and b/src/assets/question/pause.png differ
diff --git a/src/assets/question/play.png b/src/assets/question/play.png
new file mode 100755
index 0000000000000000000000000000000000000000..d9f76d79145bd783077d340cd8ea42cd617bf8ee
Binary files /dev/null and b/src/assets/question/play.png differ
diff --git a/src/assets/question/right.png b/src/assets/question/right.png
new file mode 100644
index 0000000000000000000000000000000000000000..fcf47c0827e540e014f07dc411ad9974dea43103
Binary files /dev/null and b/src/assets/question/right.png differ
diff --git a/src/assets/right.mp3 b/src/assets/right.mp3
new file mode 100644
index 0000000000000000000000000000000000000000..c02694bcae3bd70abeca0d77230e47c33a829434
Binary files /dev/null and b/src/assets/right.mp3 differ
diff --git a/src/assets/wrong.mp3 b/src/assets/wrong.mp3
new file mode 100644
index 0000000000000000000000000000000000000000..6c4b324803190f0a4c9ca28742a34c55962d74f0
Binary files /dev/null and b/src/assets/wrong.mp3 differ
diff --git a/src/styles.css b/src/styles.css
deleted file mode 100644
index 90d4ee0072ce3fc41812f8af910219f9eea3c3de..0000000000000000000000000000000000000000
--- a/src/styles.css
+++ /dev/null
@@ -1 +0,0 @@
-/* You can add global styles to this file, and also import other style files */
diff --git a/src/styles.scss b/src/styles.scss
new file mode 100644
index 0000000000000000000000000000000000000000..c1f593fa6def1161f9667969f2f00faeec36a95c
--- /dev/null
+++ b/src/styles.scss
@@ -0,0 +1,51 @@
+/* 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;
+}