使用electron開發應用項目總結

使用electron和node-ffi開發的軟件,項目常見問題總結了下

技術棧
electron // 使用 JavaScript, HTML 和 CSS 構建跨平臺的桌面應用
electron-vue // 基於 vue (基本上是它聽起來的樣子) 來構造 electron 應用程序的樣板代碼。
electron-store // 本地簡單數據存儲
electron-log // 日誌模塊
electron-updater // 更新模塊
electron-builder // 打包模塊
electron-rebuild //下載 headers、編譯原生模來重建 Electron 模塊 
ffi // 使用node加載和調用動態庫
iconv // 轉碼
node-machine-id // 獲取計算機惟一標識
vue-router // 路由
axios // http
element-ui //UI庫
常見錯誤和項目總結

1.fatal error LNK1127

刪除用戶目錄下.node-gyp 從新安裝 npm install node-gyp -gjavascript

2.項目開發環境?

node32位最新版 electron32位(npm i會自動下載32位的) dll文件32位 win10操做系統 .NET Framework 
安裝node-gyp 的前提條件以下:
A.
1.python(v2.7 ,3.x不支持);
2.visual C++ Build Tools,或者 (vs2015以上(包含15))
3..net framework 4.5.1
B.若是是乾淨的環境能夠用下面命令一鍵安裝
npm install --global --production windows-build-tools
報Python錯誤的須要 npm iconfig set python python.exe安裝位置

3.窗口最大化最小化

isMaximized()有問題 須要本身聲明flagvue

let isMaxWin = false
ipcMain.on('min', e => mainWindow.minimize())
ipcMain.on('max', e => {
  console.log('isMaxWin:' + isMaxWin)
  if (isMaxWin) {
    isMaxWin = false
    mainWindow.unmaximize()
  } else {
    mainWindow.maximize()
    isMaxWin = true
  }
})

4.node-ffi調用dll常見序錯誤

1.Win32 error 126錯誤就是:
    一般是傳入的DLL路徑錯誤,找不到Dll文件,推薦使用絕對路徑。
    若是是在x64的node/electron下引用32位的DLL,也會報這個錯,反之亦然。要確保DLL要求的CPU架構和你的運行環境相同。
    DLL還有引用其餘DLL文件,可是找不到引用的DLL文件,多是VC依賴庫或者多個DLL之間存在依賴關係。
    Dynamic Linking Error: Win32 error 127:DLL中沒有找到對應名稱的函數,須要檢查頭文件定義的函數名是否與DLL調用時寫的函數名是否相同。

2.詳細錯誤碼 https://www.jianshu.com/p/79bbb96e0065

5.寬高分辨率適應

let devInnerHeight = 1080.0 // 開發時的InnerHeight
let devDevicePixelRatio = 1.0// 開發時的devicepixelratio
let devScaleFactor = 1.3 // 開發時的ScaleFactor
let scaleFactor = require('electron').screen.getPrimaryDisplay().scaleFactor
let zoomFactor = (window.innerHeight / devInnerHeight) * (window.devicePixelRatio / devDevicePixelRatio) * (devScaleFactor / scaleFactor)
require('electron').webFrame.setZoomFactor(zoomFactor)

6.c與javascript類型對應

https://www.npmjs.com/package...

7.node-ffi 調用 dll,打包時asar爲true時報錯問題

由於asar模式下爲只讀文件,致使ffi報錯,issue上也有提問,沒有解答(https://github.com/node-ffi/n...
各類文檔查看後,發現"asarUnpack",因此纔有下曲線救國的方案java

"asarUnpack":[
      "./dist/electron", // dll文件等
      "node_modules/ffi" // ffi不壓縮
    ],
if (process.env.NODE_ENV !== 'development') {
  __static = __static.replace('app.asar', 'app.asar.unpacked')
}

8.應用自動下載更新和手動檢查更新

使用electron-updater結合electron-builder實現;細節注意渲染進程和主進程通訊時手動檢查時屢次觸發。能夠根據下載進度判斷。
//main.js
import {autoUpdater} from 'electron-updater'

// 主進程監聽渲染進程傳來手動檢查更新的信息
ipcMain.on('update', (e, arg) => {
  console.log('update')
  autoUpdater.checkForUpdates()
})

let checkForUpdates = () => {
  // 配置安裝包遠端服務器
  autoUpdater.setFeedURL('http://*******:8888/lastApp')

  // 下面是自動更新的整個生命週期所發生的事件
  autoUpdater.on('error', function (message) {
    sendUpdateMessage('error', message)
  })
  autoUpdater.on('checking-for-update', function (message) {
    sendUpdateMessage('checking-for-update', message)
  })
  autoUpdater.on('update-available', function (message) {
    sendUpdateMessage('update-available', message)
  })
  autoUpdater.on('update-not-available', function (message) {
    sendUpdateMessage('update-not-available', message)
  })

  // 更新下載進度事件
  autoUpdater.on('download-progress', function (progressObj) {
    mainWindow.webContents.send('downloadProgress', progressObj)
  })
  // 更新下載完成事件
  autoUpdater.on('update-downloaded', function (event, releaseNotes, releaseName, releaseDate, updateUrl, quitAndUpdate) {
    sendUpdateMessage('isUpdateNow')
    ipcMain.on('updateNow', (e, arg) => {
      autoUpdater.quitAndInstall()
    })
  })

  // 執行自動更新檢查
  autoUpdater.checkForUpdates()
}

// 主進程主動發送消息給渲染進程函數
function sendUpdateMessage (message, data) {
  console.log({ message, data })
  mainWindow.webContents.send('message', { message, data })
}

//app啓動時自動更新

mainWindow.on('ready-to-show', () => {
    console.log('mainWindow opened')
    mainWindow.show()
    mainWindow.focus()
    // 啓動時自動更新
    checkForUpdates()
})
渲染進程監聽更新事件UI層作出反饋
const { ipcRenderer } = require('electron')

mounted () {
    this.$router.push({name: 'main'})
    // 更新進度
    ipcRenderer.on('message', (event, {message, data}) => {
      console.log(message + ' <br>data:' + JSON.stringify(data) + '<hr>')
      switch (message) {
        case 'update-available':
          this.newVer = data.version
          this.$notify({
            title: '發現新版本',
            dangerouslyUseHTMLString: true,
            duration: 0,
            message: '當前軟件版本號:' + this.info.version + '<br>新版本版本號:' + data.version + '<br>正在下載最新版本,請稍等!',
            type: 'success'
          })
          this.show = true
          break
        case 'update-not-available':
          this.show = false
          this.$notify({
            title: '暫無新版本',
            dangerouslyUseHTMLString: true,
            duration: 0,
            message: '當前軟件版本號:' + this.info.version + '<br>當前版本爲最新版本,暫無新版本可更新!',
            type: 'success'
          })
          break
        case 'error':
          this.show = false
          this.$notify({
            title: '檢查更新錯誤',
            dangerouslyUseHTMLString: true,
            duration: 0,
            message: '請聯繫xxx,或者手動到http://xxx.com下載' ,
            type: 'error'
          })
          break
        case 'isUpdateNow':
          this.show = false
          this.$confirm('新版本下載完成, 是否繼續安裝?', '提示', {
            confirmButtonText: '肯定',
            cancelButtonText: '取消',
            closeOnClickModal: false,
            closeOnPressEscape: false,
            type: 'success'
          }).then(() => {
            this.$message({
              type: 'success',
              message: '正在安裝,請稍後!'
            })
            ipcRenderer.send('updateNow')
          }).catch(() => {
            this.$message({
              type: 'info',
              message: '已取消安裝,將在您下次使用時自動安裝新版本!'
            })
          })
          break
      }
    })

    ipcRenderer.on('downloadProgress', (event, progressObj) => {
      console.log(progressObj)
      this.percent = progressObj.percent.toFixed(2) || 0
    })
  }


   methods: {
    //手動更新方法
    updater () {
      const loading = this.$loading({
        lock: true,
        text: '獲取最新版本信息中...',
        spinner: 'el-icon-loading',
        background: 'rgba(0, 0, 0, 0.7)'
      })
      // 正在自動更新時,不發送更新指令,避免屢次下載致使下載進度多條記錄
      if (this.percent === 0) {
        ipcRenderer.send('update')
      } else {
        this.$notify({
          title: '發現新版本',
          dangerouslyUseHTMLString: true,
          duration: 0,
          message: '當前軟件版本號:' + this.info.version + '<br>正在下載最新版本,請稍等!',
          type: 'success'
        })
        this.show = true
      }

      setTimeout(() => {
        loading.close()
      }, 2000)
    },
   ..................
  }

9.程序秒開優化小技巧

由於electron是基於 Chromium 和 Node.js,因此頁面加載的白屏問題特別是使用vue開發的單頁應用。這裏能夠在實例化BrowserWindow時,把show: false而後在ready-to-show裏面調用show方法和focus方法。node

10.axios封裝

主要實現如下功能
.post 和 get 封裝 

.報錯統一處理

.post提交序列化json數據 後臺支持json可忽略

.可配置的loading 是否顯示和顯示的文字

.多個請求時合併一個loading

.統一headers提交

.開發和產品環境下api地址的切換
代碼以下
// request.js
import axios from 'axios'
import { Message } from 'element-ui'
import qs from 'qs'
import {getToken} from './cookie'
import {
  showFullScreenLoading,
  tryHideFullScreenLoading
} from './axiosLoading'
const log = require('electron-log')
const Store = require('electron-store')
const store = new Store()
log.transports.console.format = '{h}:{i}:{s} {text}'
log.transports.console.level = 'silly'
log.transports.file.maxSize = 5 * 1024 * 1024
// import store from '../store'
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8'
// 建立axios實例
const service = axios.create({
  // baseURL: process.env.BASE_API,  api的base_url
  timeout: 15000 // 請求超時時間
})

// request攔截器
service.interceptors.request.use(config => {
  if (store.get('userToken')) {
    config.headers['x-appMachineId'] = store.get('appMachineId')// 電腦的惟一識別碼 除非重裝系統 使用node-machine-id
    config.headers['x-userId'] = store.get('userToken').userId// 讓每一個請求攜帶用戶id
    config.headers['x-appArea'] = store.get('appArea')// 軟件所在區域
    config.headers['x-appDepartment'] = store.get('appDepartment')// 軟件所在部門
  }
  // 是否顯示loading動畫
  if (config.showLoading) {
    // loading動畫時下方的文字
    showFullScreenLoading(config.loadingText)
  }
  // POST 請求序列化json數據提交
  if (config.method === 'post') {
    config.data = qs.stringify(config.data)
  }
  // log文件記錄
  log.info(getToken().nickName + ' 請求了 API request->' + JSON.stringify(config))
  return config
}, error => {
  // Do something with request error
  console.log(error) // for debug
  log.error(getToken().nickName + ' 請求了 API request error->' + JSON.stringify(error))
  Promise.reject(error)
})

// respone攔截器
service.interceptors.response.use(
  response => {
    log.info('API response ok->' + JSON.stringify(response))
    /**
  * code爲非000是拋錯 可結合本身業務進行修改
  */
    if (response.config.showLoading) {
      tryHideFullScreenLoading()
    }
    let ret = response.data
    if (ret.code !== '000') {
      // 非000錯誤下是否須要根據返回值作其餘業務判斷
      if (response.config.useError) {
        return response.data
      } else {
        Message({
          message: ret.msg,
          type: 'error',
          duration: 5 * 1000
        })
      }
    } else {
      return response.data
    }
  },
  error => {
    tryHideFullScreenLoading()
    console.log('err' + error)// for debug
    log.error('API response error->' + JSON.stringify(error))
    let err = error
    if (err && err.response) {
      switch (err.response.status) {
        case 400:
          err.message = '錯誤請求'
          break
        case 401:
          err.message = '未受權,請從新登陸'
          break
        case 403:
          err.message = '拒絕訪問'
          break
        case 404:
          err.message = '請求錯誤,未找到該資源'
          break
        case 405:
          err.message = '請求方法未容許'
          break
        case 408:
          err.message = '請求超時'
          break
        case 500:
          err.message = '服務器端出錯'
          break
        case 501:
          err.message = '網絡未實現'
          break
        case 502:
          err.message = '網絡錯誤'
          break
        case 503:
          err.message = '服務不可用'
          break
        case 504:
          err.message = '網絡超時'
          break
        case 505:
          err.message = 'http版本不支持該請求'
          break
        default:
          err.message = `鏈接錯誤${err.response.status}`
      }
    } else {
      err.message = '鏈接到服務器失敗'
    }
    Message({
      message: error.message,
      type: 'error',
      duration: 5 * 1000
    })
    return Promise.reject(error)
  }
)

export default service

// axiosLoading.js
import { Loading } from 'element-ui'
import _ from 'lodash'

let needLoadingRequestCount = 0
let loading

function startLoading (text) {
  console.log('startLoading =============')
  loading = Loading.service({
    lock: true,
    text: text || 'loading...',
    background: 'rgba(0, 0, 0, 0.7)'
  })
}

function endLoading () {
  console.log('endLoading==========')
  loading.close()
}

const tryCloseLoading = () => {
  if (needLoadingRequestCount === 0) {
    endLoading()
  }
}

export function showFullScreenLoading (text) {
  if (needLoadingRequestCount === 0) {
    startLoading(text)
  }
  needLoadingRequestCount++
}

export function tryHideFullScreenLoading () {
  if (needLoadingRequestCount <= 0) return
  needLoadingRequestCount--
  if (needLoadingRequestCount === 0) {
    _.debounce(tryCloseLoading, 300)()
  }
}
//環境切換 
// webpack.renderer.config.js plugins添加
const config = require('../config/index.js')
new webpack.DefinePlugin({
      'process.env': process.env.NODE_ENV === 'production' ? config.build.env : config.dev.env
    })

//建立 config 文件下 和 dev.env.js index.js prod.env.js
//index.js
module.exports = {
  build: {
    env: require('./prod.env')
  },
  dev: {
    env: require('./dev.env')
  }
}

// dev.env.js
module.exports = {
  NODE_ENV: '"development"',
  BASE_API: '"https://www.easy-mock.com/xxxx/test"', // 系統接口
  BASE_API_JKPT: '"http://xxxx:8080/ims/API"' // 接口平臺
}

//prod.env.js
module.exports = {
  NODE_ENV: '"production"',
  BASE_API: '"https://www.easy-mock.com/mock/xx/test"', // 系統接口
  BASE_API_JKPT: '"https://easy-mock.com/mock/xx/vue-admin"' // 接口平臺
}

// 使用示例
import request from '@/tools/request'

export function getBaseData () {
  console.log(process.env)

  return request({
    url: process.env.BASE_API + '/baseData',// 根據環境變量自動切換
    method: 'get',
    showLoading: true,
    loadingText: '同步基礎數據中',
    useError: true
  })
}

11.調用dll中文亂碼問題

let Iconv = require('iconv').Iconv
// 讀取卡信息
ds.iReadCardBas = function () {
  let info = Buffer.alloc(500)
  let ret = dsDDL.xxx(1, info) // dll xxx方法名
  let iconv = new Iconv('GBK', 'UTF-8') // 轉碼
  let retstring = iconv.convert(info).toString()
  if (ret === 0) {
    return retstring
  } else {
    dialog.showErrorBox('錯誤提示', retstring)
  }
}

12.封裝拍照模塊

使用mediaDevices 由於運行環境爲谷歌瀏覽器因此不考慮兼容問題 參考。已封裝爲cam組件,父組件調用this.$refs.camera.snapshot()方法拍照
<template>
  <div class="cam"> 
     <el-alert
    title="拍攝要求:1.正臉拍照 2.白色背景 3.請勿佩戴帽子或眼鏡 4.請參照綠色邊框輔助線拍攝"
    type="info"
    show-icon>
  </el-alert>
      <img :src="imageUrl" alt="">
      <video ref="video" class="_vc-video vc-video" :style="videoStyle" :width="width" :height="height" webkit-playsinline></video>
      <canvas ref="canvas" :width="width" :height="height" v-show="false"></canvas>
  </div>
</template>

<script>
export default {
  name: 'Cam',
  data () {
    return {
      imageUrl: 'static/outline.png'
    }
  },
  props: {
    autoplay: {
      type: Boolean,
      default: true
    },
    width: {
      type: Number,
      default: 640
    },
    height: {
      type: Number,
      default: 480
    },
    fit: {
      type: String,
      default: 'cover',
      validator: function (value) {
        return ['cover', 'contain'].indexOf(value) !== -1
      }
    }
  },
  computed: {
    video () {
      return this.$refs.video
    },
    videoStyle () {
      return {
        'object-fit': this.fit
      }
    }
  },
  methods: {

    calculateVideoPreviewSize () {
      let video = this.$refs.video
      var videoPreviewSize = {
        width: video.videoWidth,
        height: video.videoHeight
      }

      let previewRatio = this.width / this.height
      let videoRatio = video.videoWidth / video.videoHeight

      // Set video preview size with width as a reference
      let widthFirst = () => {
        videoPreviewSize.width = this.width
        videoPreviewSize.height = video.videoHeight * this.width / video.videoWidth
      }

      // Set video preview size with height as a reference
      let heightFirst = () => {
        videoPreviewSize.height = this.height
        videoPreviewSize.width = video.videoWidth * this.height / video.videoHeight
      }

      if (this.fit === 'contain') {
        if ((previewRatio < 1 && videoRatio < 1) || (previewRatio <= 1 && videoRatio >= 1)) {
          widthFirst()
        } else {
          heightFirst()
        }
      } else if (this.fit === 'cover') {
        if ((previewRatio < 1 && videoRatio < 1) || (previewRatio <= 1 && videoRatio >= 1)) {
          heightFirst()
        } else {
          widthFirst()
        }
      }

      return videoPreviewSize
    },
    snapshot () {
      var video = this.$refs.video
      var canvas = this.$refs.canvas
      var context = canvas.getContext('2d')

      var videoPreviewSize = this.calculateVideoPreviewSize()

      // Draw the image
      let topOffset = (this.height - videoPreviewSize.height) / 2
      let leftOffset = (this.width - videoPreviewSize.width) / 2
      context.drawImage(video, leftOffset, topOffset, this.width - leftOffset * 2, this.height - topOffset * 2)

      // Return a base 64 snapshot
      return canvas.toDataURL('image/jpeg', 0.8)
    }
  },
  mounted () {
    var video = this.$refs.video
    let _this = this
    function updateDeviceList () {
      navigator.mediaDevices.enumerateDevices()
        .then(function (devices) {
          let haveok = false
          devices.forEach(function (device) {
            if (device.kind === 'videoinput') {
              haveok = true
              _this.$notify({
                title: '攝像頭鏈接成功',
                message: device.label,
                type: 'success'
              })

              navigator.mediaDevices.getUserMedia({
                video: true
              })
                .then(stream => {
                  video.srcObject = stream

                  if (_this.autoplay) {
                    video.play()
                  }
                })
                .catch(error => {
                  _this.$notify.error({
                    title: '拍照模塊錯誤',
                    message: error
                  })
                })
            }
          })
          if (haveok === false) {
            _this.$notify({
              title: '錯誤',
              message: '無鏈接攝像頭,請查看攝像頭鏈接是否正常可用',
              type: 'error'
            })
          }
        })
    }
    // Get permission to use the camera
    if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
      // 插入或移除設備時監聽作出提示
      navigator.mediaDevices.ondevicechange = function (event) {
        updateDeviceList()
      }
      navigator.mediaDevices.getUserMedia({
        video: true
      })
        .then(stream => {
          video.srcObject = stream

          if (_this.autoplay) {
            video.play()
          }
        })
        .catch(error => {
          _this.$notify.error({
            title: '拍照模塊錯誤',
            message: error
          })
        })
    }
  }
}
</script>

<style lang="less" scoped>
.cam{
    position: relative;
    width: 100%;
    height: 500px;
    video,img{
        position: absolute;
        left: 100px;
        top: 43px;
    }
    img{
        z-index: 5;
    }
}
</style>

13.封裝圖像實時處理

封裝的glfx.js,Caman JS也是個不錯的選擇。須要注意的是,項目若是開啓eslint,js庫會報錯,開始在js文件頭部添加/ eslint-disable /忽略庫文件的校驗。封裝的經常使用方法,亮度、對比度、色相、飽和度、天然飽和度、降噪點、銳化、噪點等
<template>
  <div class="front-process-container" v-loading="loading">
        <el-row :gutter="20">
            <el-col :span="12"><div class="grid-content">
                <p>拍攝照片</p>
                <img id="original" style="width:100%" crossorigin="anonymous" :src="imgUrl" ref="originalImg">
                </div></el-col>
            <el-col :span="12"><div class="grid-content">
                 <p>優化後照片</p>
                <img style="width:100%" crossorigin="anonymous" :src="newSrc">
                </div></el-col>
        </el-row>
        <el-row :gutter="20">
            <el-col :span="12">
            <div class="slider-wrap">
                <label class="slider-label">亮度</label>
                <el-slider v-model="valueOfBrightness" class="zj-slider" :max="100" :min="-100" @change="drawByParams"></el-slider>
            </div>
            </el-col>
            <el-col :span="12">
             <div class="slider-wrap">
                <label class="slider-label">對比度</label>
                <el-slider v-model="valueOfContrast" class="zj-slider" :max="100" :min="-100" @change="drawByParams"></el-slider>
            </div>
            </el-col>
        </el-row> 
        <el-row :gutter="20">
            <el-col :span="12">
             <div class="slider-wrap">
                <label class="slider-label">色調</label>
                <el-slider v-model="valueOfHue" class="zj-slider" :max="100" :min="-100" @change="drawByParams"></el-slider>
            </div>
            </el-col>
          <el-col :span="12">
            <div class="slider-wrap">
                <label class="slider-label">飽和度</label>
                <el-slider v-model="valueOfSaturation" class="zj-slider" :max="100" :min="-100" @change="drawByParams"></el-slider>
            </div>
            </el-col>
        </el-row>
       <el-row :gutter="20">
            <el-col :span="12">
             <div class="slider-wrap">
                <label class="slider-label">加噪</label>
                <el-slider v-model="valueOfNoise" class="zj-slider" :max="100" :min="0" @change="drawByParams"></el-slider>
            </div>
            </el-col>
          <el-col :span="12">
            <div class="slider-wrap">
                <label class="slider-label">深褐</label>
                <el-slider v-model="valueOfSepia" class="zj-slider" :max="100" :min="0" @change="drawByParams"></el-slider>
            </div>
            </el-col>
        </el-row>
        <el-row :gutter="20">
            <el-col :span="12">
              <div class="slider-wrap">
                <label class="slider-label">銳化半徑</label>
                <el-slider v-model="usmRadius" class="zj-slider" :max="200" :min="0" @change="drawByParams"></el-slider>
            </div>
            </el-col>
          <el-col :span="12">
              <div class="slider-wrap">
                <label class="slider-label">銳化強度</label>
                <el-slider v-model="usmStrength" class="zj-slider" :max="5" :min="0" :step="0.01" @change="drawByParams"></el-slider>
            </div>
            </el-col>
        </el-row>
    
        <el-button class="op-btn" @click="drawByParams('denoise')" :class="{active: isDenoise}">降噪</el-button>
        <el-button class="op-btn" @click="resetImg">恢復原圖</el-button>
  </div>
</template>

<script>
import * as _ from 'underscore'
import {fx} from '@/components/camera/glfx.js'
export default {
  data () {
    return {
      valueOfBrightness: 0,
      _canvas: null,
      _texture: null,
      imgElement: null,
      _draw: null,
      newSrc: '',
      OKIMG: '',
      originalSrc: '',
      valueOfContrast: 0,
      isCurves: false,
      isDenoise: false,
      valueOfHue: 0,
      valueOfSaturation: 0,
      valueOfNoise: 0,
      valueOfSepia: 0,
      usmRadius: 0,
      usmStrength: 0,
      loading: true,
      opBtn: []
    }
  },
  props: {
    imgUrl: {
      required: true
    }
  },
  methods: {
    drawByParams (operation) {
      this.loading = true
      this.resetProperty()
      let opIndex = _.indexOf(this.opBtn, operation)
      if (opIndex !== -1) {
        this.opBtn.splice(opIndex, 1)
      } else {
        this.opBtn.push(operation)
      }
      this._draw
        .brightnessContrast(this.valueOfBrightness / 100, this.valueOfContrast / 100)
        .hueSaturation(this.valueOfHue / 100, this.valueOfSaturation / 100)
        .noise(this.valueOfNoise / 100)
        .sepia(this.valueOfSepia / 100)
        .unsharpMask(this.usmRadius, this.usmStrength)
      if (_.indexOf(this.opBtn, 'curves') !== -1) {
        this.isCurves = true
        this._draw.curves([[0, 1], [1, 0]], [[0, 1], [1, 0]], [[0, 1], [1, 0]])
      } else {
        this.isCurves = false
      }
      if (_.indexOf(this.opBtn, 'denoise') !== -1) {
        this.isDenoise = true
        this._draw.denoise(20)
      } else {
        this.isDenoise = false
      }
      this._draw.update()

      console.log(this._draw)
      console.log(this._canvas)

      this.newSrc = this._canvas.toDataURL('image/jpeg', 0.8)
      this.setImg()
      this.loading = false
    },
    // 對照片進行358X441像素處理
    setImg () {
      let imageisok = new Image()
      imageisok.src = this.newSrc
      imageisok.onload = () => {
        let c = document.createElement('canvas')
        c.width = 358
        c.height = 441
        var context = c.getContext('2d')
        context.drawImage(imageisok, 0, 0, 358, 441)
        this.OKIMG = c.toDataURL('image/jpeg')
      }
    },
    returnPhoto () {
      return this.OKIMG
    },
    getWebGLElements () {
      console.log('getWebGLElements')
      if (!this._canvas) {
        this._canvas = fx.canvas()
      }
      console.log(this._canvas)
      if (!this.imgElement) {
        this.imgElement = this.$refs.originalImg
        this.originalSrc = this.imgElement.src
      }

      if (!this._texture) {
        this._texture = this._canvas.texture(this.imgElement)
      }

      if (!this._draw) {
        this._draw = this._canvas.draw(this._texture)
      }
      this.loading = false
    },
    resetProperty () {
      console.log('resetProperty:', this.imgElement)
      console.log(this._texture)
      this._texture.loadContentsOf(this.imgElement)
      this._draw = this._canvas.draw(this._texture)
    },
    resetImg () {
      this.valueOfBrightness = 0
      this.valueOfContrast = 0
      this.valueOfHue = 0
      this.valueOfSaturation = 0
      this.valueOfNoise = 0
      this.valueOfSepia = 0
      this.usmRadius = 0
      this.usmStrength = 0
      this.isCurves = false
      this.isDenoise = false
      this.opBtn = []
      this.resetProperty()
      this._draw.update()
      this.newSrc = this._canvas.toDataURL('image/jpeg', 0.8)
    }
  },
  mounted () {
    this.loading = false
    var img = new Image()
    img.setAttribute('crossOrigin', 'anonymous')
    img.onload = () => {
      this.$nextTick(() => {
        this.getWebGLElements()
      })
    }

    img.src = this.imgUrl
    this.newSrc = this.imgUrl
    this.setImg()
  },
  watch: {
    imgUrl: function (val) {
      if (val) {
        var img = new Image()
        img.setAttribute('crossOrigin', 'anonymous')
        img.onload = () => {
          this.$nextTick(() => {
            this.getWebGLElements()
          })
        }
        img.src = val
        this.newSrc = val
        this.setImg()
      }
    }
  }
}
</script>
<style lang="less" scoped>

</style>
相關文章
相關標籤/搜索