一塊兒用Typescript來寫一個圖片全屏預覽組件吧

簡介

2020年是一個元年,有武漢疫情告急,企業停工。所幸的是,開發人員能夠在家辦公,不幸的是,工資全憑情懷,網速還得把電腦伸到窗外來執行一下git pull ...。不扯皮了,近期不管是工做項目仍是我的項目都須要用到一些簡單的全屏預覽的需求,因此仍是動手寫一個吧,之後可能還會用到的。功能還不是很完善,後期慢慢更新。css

Tip

開發框架: Vue.js + Typescripthtml

腳手架: Vue-clivue

icon: iconfont + iview iconios

傳參Props

參數名稱 參數類型 是否必傳 示例
path 字符數組,字符串 ['url1', 'url2'], 'url'
isVisible 布爾值(真假) true / false

xmind腦圖

圖片預覽

我沒有去作圖片的一些高度和寬度更改,由於大圖的話後臺給的數據圖片都是60%的比例。 git

ee198888.png

實現思路

顯示&&隱藏

接收傳遞過來的isVisible來控制組件容器的加載,同時關閉按鈕傳遞false上去,表明我須要關閉,上層的isVisible綁定的值做出修改,這樣就實現了一個顯示隱藏的效果,代碼以下:web

父組件

<ZoomImage :path="defaultImagePath" :isVisible.sync="visible" />
複製代碼

子組件

<Icon type="md-close" class="default-btn close-zoom" @click="$emit('update:isVisible', false)"/>
複製代碼

圖片加載展現

由於傳遞的path參數存在字符串和數組兩種可能。因此展現的時候,須要去進行判斷,若是是數組參數,那麼就須要去進行判斷何時顯示下一張箭頭,何時顯示上一張箭頭,邏輯代碼以下:typescript

<!-- HTMLElement DOM -->
<Icon v-show="isNextShow" type="ios-arrow-forward" class="default-btn leave-next" @click.stop="toNext"/>
<div class="img-wrapper" v-loading="isSuccess">
  <div class="error-view" v-show="isError">
    <Icon type="md-bug" :size="30"/>
    <p>加載失敗</p>
  </div>
  <img v-show="!isError" :src="setImagePath" alt="圖片" id="zoomImage" @load="imgLoad('success')" @error="imgLoad('error')">
</div>
複製代碼
/* path展現 */
private isSuccess: boolean = true // 加載是否成功
private isError: boolean = false // 加載是否失敗
private currentPathIndex: number = 0 // 若是path爲數組,默認的index

/* 設置當前顯示圖片屬性 */
get setImagePath() {
  // 若是path的類型是字符串, 就直接使用path自己, 若是不是就使用數組path[0]數組第一條
  return typeof this.path === 'string' ? this.path : this.path[this.currentPathIndex]
}

/* * 偵聽圖片是否加載完成 ? 這裏還須要就是去作v-loading的顯示和關閉,這個指令的簡單方式實現。百度不少。各位能夠自定義或者不用 * 我在img圖片上綁定了load 和 error事件,經過傳遞的字符串來共用一個方法,這樣就不須要寫兩個不一樣的methods了。只是作的操做不一樣了 * isError是用來作加載失敗的展現的。防止沒有圖片的時候黑屏尷尬 */
private imgLoad(state: string): void {
  if (state === 'success') {
    // success
    this.isSuccess = false
  } else {
    this.isSuccess = false
    this.isError = true
  }
}
複製代碼

工具欄

工具欄方法,如今我只作了這一些經常使用的操做,縮放,旋轉,保存(google內核問題,在想新的解決方案,如何去處理),恢復。旋轉默認是90度,縮放默認10%。這是一個比較合理的參數了。工具欄我是經過for循環出來的。而後共同綁定了一個methods方法,不須要去寫5個方法分開實現相應功能,只須要根據你for的index來去作switch的選擇處理。數組

<ul class="tool-wrapper">
  <li v-for="(item, index) in toolIcon" :key="index" class="tool-item">
    <i class="iconfont tool-item__icon" :class="item" @click.stop="bindBlowClick(index)"/>
  </li>
</ul>
複製代碼
/* 變量,這是定義工具欄的圖標,使用的是iconfont的styles圖標 */
private toolIcon: Array<string> = ['icon-rotateright', 'icon-rotateleft', 'icon-download', 'icon-suoxiao', 'icon-fangda', 'icon-emoji']

  /* 綁定工具欄相應方法, 作一些處理 */
  private bindBlowClick(id: number): void{
    // 默認值
    let rotateSize: number = 0
    let scaleSize: number = 1
    // 獲取dom
    let imgDom: any = document.getElementById('zoomImage')
    // 獲取dom參數數組,切割
    let DomArr: Array<string> = imgDom.style.transform.split(' ')
    if (DomArr.length >= 2) {
      // 利用正則獲取當前的transform屬性
      scaleSize = Number(DomArr[0].replace(/[^\d|^.]/g, ''))
      rotateSize = Number(DomArr[1].replace(/[^\d|^-]/g, ''))
    }
    switch (id) {
      case 0:
        // 順時針旋轉
        imgDom.style['transform'] = `scale(${scaleSize}) rotate(${(rotateSize === 360 ? 0 : rotateSize) + 90}deg)`
        break
      case 1:
        // 逆時針旋轉
        imgDom.style['transform'] = `scale(${scaleSize}) rotate(${(rotateSize === 360 ? 0 : rotateSize) - 90}deg)`
        break
      case 2:
        // 保存/下載圖片
        const aDom: HTMLAnchorElement = document.createElement('a')
        const aDomEvent: MouseEvent = new MouseEvent('click')
        aDom.download = 'download'
        aDom.href = this.path[this.currentPathIndex]
        aDom.dispatchEvent(aDomEvent)
        break
      case 3:
        // 縮小
        console.log(scaleSize)
        imgDom.style['transform'] = `scale(${scaleSize - 0.1}) rotate(${rotateSize})`
        break
      case 4:
        // 放大
        imgDom.style['transform'] = `scale(${scaleSize + 0.1}) rotate(${rotateSize}deg)`
        break
      case 5:
        // 重置
        imgDom.style['transform'] = `scale(1) rotate(0deg)`
        break
    }
  }
複製代碼

貼一下代碼吧

<!-- Template -->
<template>
  <div class="zoom-image-container" v-if="isVisible">
    <Icon type="md-close" class="default-btn close-zoom" @click="$emit('update:isVisible', false)"/>
    <!-- <Icon type="ios-arrow-back" class="default-btn leave-last"/> -->
    <Icon v-show="isNextShow" type="ios-arrow-forward" class="default-btn leave-next" @click.stop="toNext"/>
    <div class="img-wrapper" v-loading="isSuccess">
      <div class="error-view" v-show="isError">
        <Icon type="md-bug" :size="30"/>
        <p>加載失敗</p>
      </div>
      <img v-show="!isError" :src="setImagePath" alt="圖片" id="zoomImage" @load="imgLoad('success')" @error="imgLoad('error')">
    </div>
    <ul class="tool-wrapper">
      <li v-for="(item, index) in toolIcon" :key="index" class="tool-item">
        <i class="iconfont tool-item__icon" :class="item" @click.stop="bindBlowClick(index)"/>
      </li>
    </ul>
  </div>
</template>
複製代碼
/* JS/TS代碼 */
<script lang="ts">
import { Component, Vue, Prop } from 'vue-property-decorator'

@Component
export default class ZoomImage extends Vue {
  // 傳入圖片數據
  @Prop({ required: true })
  private path!: string | Array<string>
  // 傳入顯示
  @Prop({ default: false, required: true })
  private isVisible!: boolean
  private toolIcon: Array<string> = ['icon-rotateright', 'icon-rotateleft', 'icon-download', 'icon-suoxiao', 'icon-fangda', 'icon-emoji']
  private isSuccess: boolean = true
  private isError: boolean = false
  private currentPathIndex: number = 0
  private imgPath: Array<string> = ['http://a4.att.hudong.com/21/09/01200000026352136359091694357.jpg']

  // 設置當前顯示圖片屬性
  get setImagePath() {
    return typeof this.path === 'string' ? this.path : this.path[this.currentPathIndex]
  }

  // 設置next按鈕是否顯示
  get isNextShow() {
    return typeof this.path !== 'string' && this.path.length > 1
  }

  // 綁定工具欄相應方法
  private bindBlowClick(id: number): void{
    let imgDom: any = document.getElementById('zoomImage')
    let DomArr: Array<string> = imgDom.style.transform.split(' ')
    let rotateSize: number = 0
    let scaleSize: number = 1
    if (DomArr.length >= 2) {
      scaleSize = Number(DomArr[0].replace(/[^\d|^.]/g, ''))
      rotateSize = Number(DomArr[1].replace(/[^\d|^-]/g, ''))
    }
    switch (id) {
      case 0:
        // 順時針旋轉
        imgDom.style['transform'] = `scale(${scaleSize}) rotate(${(rotateSize === 360 ? 0 : rotateSize) + 90}deg)`
        break
      case 1:
        // 逆時針旋轉
        imgDom.style['transform'] = `scale(${scaleSize}) rotate(${(rotateSize === 360 ? 0 : rotateSize) - 90}deg)`
        break
      case 2:
        // 保存/下載圖片
        const aDom: HTMLAnchorElement = document.createElement('a')
        const aDomEvent: MouseEvent = new MouseEvent('click')
        aDom.download = 'download'
        aDom.href = this.path[this.currentPathIndex]
        aDom.dispatchEvent(aDomEvent)
        break
      case 3:
        // 縮小
        console.log(scaleSize)
        imgDom.style['transform'] = `scale(${scaleSize - 0.1}) rotate(${rotateSize})`
        break
      case 4:
        // 放大
        imgDom.style['transform'] = `scale(${scaleSize + 0.1}) rotate(${rotateSize}deg)`
        break
      case 5:
        // 放大
        imgDom.style['transform'] = `scale(1) rotate(0deg)`
        break
    }
  }
  
  // 偵聽圖片是否加載完成
  private imgLoad(state: string) {
    if (state === 'success') {
      // success
      this.isSuccess = false
    } else {
      this.isSuccess = false
      this.isError = true
    }
  }

  // 下一張
  private toNext(): void {
    const pathLength: number = this.path.length
    this.currentPathIndex === pathLength - 1 ? this.currentPathIndex = 0 : this.currentPathIndex++
  }
}
</script>
複製代碼
/* styles */
<style lang="scss">
@import '@/assets/scss/global.d.scss';
  .zoom-image-container{
    @include position($position: fixed, $top: 0, $left: 0);
    @include zIndex($zindex: 2000);
    @include flex($justify: center, $align: center);
    // background: rgba(0, 0, 0, 0.8);
    background: black;
    height: 100vh;
    width: 100vw;
    .leave-last{
      @include position($position: absolute, $left: rem(50px), $top: 50%);
    }
    .leave-next{
      @include position($position: absolute, $right: rem(50px), $top: 50%);
    }
    .default-btn{
      @include radius(50%);
      font-size: rem(25px);
      padding: rem(15px);
      font-weight: bold;
      color: $bg;
      background: #222222;
      cursor: pointer;
      &:hover{
        color: $main;
      }
    }
    .close-zoom{
      @include position($position: absolute, $right: rem(50px), $top: rem(50px));
    }
    .img-wrapper {
      #zoomImage{
        // defalut css
        transform: rotate(0deg) scale(1);
      }
      .error-view{
        width: rem(400px);
        height: rem(400px);
        background: $bg;
        cursor: pointer;
        @include flex($justify: center, $align: center, $direction: column);
        p{
          font-size: rem(20px);
        }
      }
    }
    .tool-wrapper{
      @include position($position: absolute, $bottom: rem(20px), $left: 50%);
      transform: translate(-50%);
      @include flex();
      .tool-item{
        background: #222222;
        &__icon{
          font-size: rem(30px);
          color: $bg;
          cursor: pointer;
          padding: rem(10px);
          &:hover{
            color: $main;
          }
        }
      }
    }
  }
</style>
複製代碼

總結

但願評論區的朋友可以提出一些寶貴的建議,若是合理的功能我會盡力去實現的。sass

2020年想給本身的博客文章多寫一點,可是不知道寫點什麼,leetcode吧,只是會簡簡單單的題目,技術深度,又沒有什麼理解深刻的技術,不過之後會慢慢積累本身的技術,分享本身和代碼的故事,2020咱們繼續努力。文章我是寫在語雀上的。沒有去作多的站點。之前搭建兩個blog都沒有堅持下來。 語雀地址app

也歡迎一塊兒作交流哦。

98e83a6b.png
相關文章
相關標籤/搜索