亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何使用小程序canvas寫一個簡單的圖片應用

發布時間:2020-12-17 09:25:30 來源:億速云 閱讀:268 作者:小新 欄目:移動開發

這篇文章主要介紹如何使用小程序canvas寫一個簡單的圖片應用,文中介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們一定要看完!

應用展示

截圖

如何使用小程序canvas寫一個簡單的圖片應用

需求

既然是小應用,那就希望最終成品是有 適用的場景 且是 有價值

需求來源

這個應用需求的靈感

在以前工作生活中,經常會無意中獲得同事的 美照

這時我們想要把這張照片做成表情包

一般給圖片添加幾個說明文字

一個有意思的溝通工具(表情包)就完成了

需求分析

基于以上需求的拆解

可以將要應用功能實現整理一下

  • 用戶需要上傳一張圖片

  • 可以添加文字

  • 文字可以作 樣式調整旋轉縮放

  • 另外我們希望還可以插入一些貼圖

  • 貼圖可以做 旋轉縮放

  • 用戶導出圖片到相冊

實現

github倉庫 https://github.com/luosijie/f...

如果喜歡我的項目,歡迎給個星星鼓勵一下

這個應用是用小程序開發的

  • 使用框架:mpx

  • 使用技術:小程序canvas

狀態管理

import { createStore } from '@mpxjs/core'

const store = createStore({
  state: {
    cavas: null,         // cnavas實例
    ctx: null,           // canvas上下文實例
    elements: [],        // canvas元素
    activeIndex: null,   // 當前編輯中的元素索引
    mode: 'background',  // 當前編輯模式:background, text, sticker
    fontStyle: {         // 文字默認樣式
      opacity: 1,
      fillStyle: '#000000',
      strokeStyle: '#000000'
    }
  },
  mutations: {
    setCanvas (state, data) {
      state.canvas = data
    },
    setCtx (state, data) {
      state.ctx = data
    },
    setElements (state, data) {
      state.elements = data
    },
    setMode (state, data) {
      state.mode = data
    },
    setActiveIndex (state, data) {
      state.activeIndex = data
    },
    setFontStyle (state, { key, data }) {
      state.fontStyle[key] = data
    },
    // 添加文字
    addText (state) {
      const size = 50
      const string = '請輸入文字'
      const text = {
        type: 'text',
        data: string,
        scale: 1,
        size,
        left: 100,
        top: 100,
        rotate: 0,
        opacity: state.fontStyle.opacity,
        fillStyle: state.fontStyle.fillStyle,
        strokeStyle: state.fontStyle.strokeStyle
      }
      state.elements.push(text)
      state.activeIndex = state.elements.length - 1
    },
    // 添加貼圖
    addSticker (state, data) {
      state.elements.push(data)
      state.activeIndex = state.elements.length - 1
    },
    // 刪除當前選中
    deleteActiveELement (state) {
      state.elements.splice(state.activeIndex, 1)
      state.activeIndex = null
    },
    // 清空畫布
    clear (state) {
      state.elements = []
      state.activeIndex = null
    }
  }
})

export default store

畫布初始化

// 初始化畫布
async initCanvas() {
  const query = this.createSelectorQuery()
  query
    .select('#canvas')
    .fields({ node: true, size: true })
    .exec(async res => {
      const canvas = res[0].node
      const ctx = canvas.getContext('2d')
      store.commit('setCanvas', canvas)
      store.commit('setCtx', ctx)

      await this.loadImage('/images/icon-rotate.png').then(res => {
        this.image.rotate = res
      })

      canvas.width = res[0].width * this.dpr
      canvas.height = res[0].height * this.dpr
      ctx.scale(this.dpr, this.dpr)
      this.drawGrid()
    })
}

繪制圖片

/**
 * 繪制圖片
 * @param { Object } ele canvas元素
 */
drawImage(ele) {
  this.ctx.save()
  const width = ele.width
  const height = ele.height
  const centerX = ele.left + ele.width / 2
  const centerY = ele.top + ele.height / 2
  this.ctx.translate(centerX, centerY)
  this.ctx.rotate(ele.rotate)
  this.ctx.drawImage(ele.data, ele.left - centerX, ele.top - centerY, width, height)
  this.ctx.restore()
}

繪制文字

/**
 * 繪制文字
 * @param { Object } ele canvas元素
 */
drawText(ele) {
  this.ctx.save()
  const width = ele.size * ele.data.length
  const height = ele.size
  const centerX = ele.left + width / 2
  const centerY = ele.top + height / 2
  this.ctx.translate(centerX, centerY)
  this.ctx.rotate(ele.rotate)
  this.ctx.font = `${ele.size}px bold sans-serif`
  this.ctx.globalAlpha = ele.opacity
  this.ctx.fillStyle = ele.fillStyle
  this.ctx.strokeStyle = ele.strokeStyle
  // this.ctx.lineWidth = 2
  this.ctx.textBaseline = 'top'
  console.log('draw-text', ele)
  this.ctx.fillText(ele.data, ele.left - centerX, ele.top - centerY)
  this.ctx.strokeText(ele.data, ele.left - centerX, ele.top - centerY)
  this.ctx.restore()
}

繪制控制元件

initController(ele) {
  const cs = this.convert2ControllerSize(ele)
  this.ctx.save()
  this.ctx.strokeStyle = '#eee'
  this.ctx.translate(cs.centerX, cs.centerY)
  this.ctx.rotate(cs.rotate)
  // 繪制虛線邊框
  this.ctx.setLineDash([10, 5], 5)
  this.ctx.strokeRect(cs.left - cs.centerX, cs.top - cs.centerY, cs.width, cs.height)
  // 繪制控制點-旋轉
  this.ctx.drawImage(this.image.rotate, cs.left + cs.width - 10 - cs.centerX, cs.top + cs.height - 10 - cs.centerY, 20, 20)
  this.ctx.restore()
}

畫布渲染函數

// 畫布渲染函數
renderCanvas() {
  this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height)
  this.drawGrid()
  console.log('draw-background', this.background)
  if (this.background) this.drawImage(this.background)
  for (let i = 0; i < this.elements.length; i++) {
    const ele = this.elements[i]
    // 渲染背景
    if (ele.type === 'background') {
      this.drawImage(ele)
    }
    if (ele.type === 'sticker') {
      this.drawImage(ele)
    }
    // 渲染文字
    if (ele.type === 'text') {
      this.drawText(ele)
    }
    // 選中元素添加控制元件
    if (this.activeIndex === i) {
      this.initController(ele)
    }
  }
}

事件監聽

移動

// 移動事件綁定函數
handleMove(e) {
  console.log('mouse-move', e)
  if (e.touches.length > 1) return
  const x = e.touches[0].x
  const y = e.touches[0].y
  const dx = this.startTouches[0].x - x
  const dy = this.startTouches[0].y - y
  const elements = this.elements.slice()
  elements[this.activeIndex || 0].left = this.startSelected.left - dx
  elements[this.activeIndex || 0].top = this.startSelected.top - dy
  store.commit('setElements', elements)
}
旋轉
// 旋轉綁定函數
handleRotate(e) {
  console.log('handleRotate')
  const start = this.startTouches[0]
  const end = e.touches[0]
  const center = {
    x: this.startSelected.centerX,
    y: this.startSelected.centerY
  }
  const startLength = Math.sqrt((center.x - start.x) ** 2 + (center.y - start.y) ** 2)
  const endLength = Math.sqrt((center.x - end.x) ** 2 + (center.y - end.y) ** 2)
  const radian = this.convert2Radian(start, end, center)
  const scale = endLength / startLength
  const elements = this.elements.slice()
  const selected = elements[this.activeIndex]
  // 旋轉
  selected.rotate = this.startSelected.rotate - radian
  // 縮放
  if (selected.type === 'text') {
    selected.left = this.startSelected.centerX - this.startSelected.size * this.startSelected.data.length * scale / 2
    selected.top = this.startSelected.centerY - this.startSelected.size * scale / 2
    selected.size = this.startSelected.size * scale
  }
  if (selected.type === 'sticker') {
    selected.left = this.startSelected.centerX - this.startSelected.width * scale / 2
    selected.top = this.startSelected.centerY - this.startSelected.height * scale / 2
    selected.width = this.startSelected.width * scale
    selected.height = this.startSelected.height * scale
  }
  store.commit('setElements', elements)
}
縮放
// 縮放事件綁定函數
handleScale(e) {
  if (e.touches.length !== 2 || this.mode !== 'background') return
  const startLength = Math.sqrt(
    (this.startTouches[0].x - this.startTouches[1].x) ** 2 +
      (this.startTouches[0].y - this.startTouches[1].y) ** 2
  )
  const endLength = Math.sqrt(
    (e.touches[0].x - e.touches[1].x) ** 2 + (e.touches[0].y - e.touches[1].y) ** 2
  )
  const scale = endLength / startLength
  const elements = this.elements.slice()
  const selected = elements[this.activeIndex || 0]
  selected.left = this.startSelected.centerX - this.startSelected.width * scale / 2
  selected.top = this.startSelected.centerY - this.startSelected.height * scale / 2
  selected.width = this.startSelected.width * scale
  selected.height = this.startSelected.height * scale
  // elements[this.activeIndex || 0].scale = this.startSelected.scale * scale
  store.commit('setElements', elements)
}
導出圖片
export() {
  if (!store.state.elements.length) {
    wx.showToast({
      title: '加點東西再導出吧',
      icon: 'none'
    })
    return
  }
  wx.showModal({
    title: '提示',
    content: '圖片將保存到手機相冊',
    success(res) {
      if (res.confirm) {
        console.log('export-canvas', store.state.ctx)
        const canvas = store.state.canvas
        wx.canvasToTempFilePath({
          x: 0,
          y: 0,
          width: canvas.width,
          height: canvas.height,
          canvas,
          complete(res) {
            if (res.errMsg === 'canvasToTempFilePath:ok') {
              wx.saveImageToPhotosAlbum({
                filePath: res.tempFilePath,
                success(res) {
                  wx.showToast({
                    title: '圖片保存成功',
                    icon: 'none'
                  })
                }
              })
            }
          }
        })
      }
    }
  })
}

以上是“如何使用小程序canvas寫一個簡單的圖片應用”這篇文章的所有內容,感謝各位的閱讀!希望分享的內容對大家有幫助,更多相關知識,歡迎關注億速云行業資訊頻道!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

宁都县| 启东市| 连江县| 闽侯县| 兴义市| 汤阴县| 宁国市| 伽师县| 聊城市| 东平县| 湟源县| 定南县| 犍为县| 涿鹿县| 汝南县| 西昌市| 定日县| 新疆| 通江县| 定兴县| 余江县| 千阳县| 康马县| 靖远县| 托里县| 武功县| 大渡口区| 江口县| 珲春市| 保康县| 即墨市| 永吉县| 陇西县| 上杭县| 广饶县| 无极县| 桂林市| 建瓯市| 遵义县| 运城市| 沂南县|