canvas-straw.vue :吸取颜色组件 

<template>
  <div class="color-content" :style="{'--size': boxSize + 'px', '--pix-size': '10px'}" v-if="value">
    <div class="close-icon" @click="close">close</div>
      <div class="img-box">
        <img :src="src" ref="img" crossOrigin  @load="initCanvas()" alt="origin-img">
      </div>
    <div class="pix-box" :style="pixPos">
      <div class="center" :style="{'borderColor': `rgb(${color})`}"></div>
      <div class="htmls" v-html="innerVal"></div>
    </div>
  </div>
</template>
<script>
export default {
  name: 'canvas-straw',
  model: {
    event: 'on-change',
    prop: 'value'
  },
  props: {
    boxSize: {
      type: Number,
      default: 100
    },
    value: {
      type: Boolean,
      default: false
    },
    src: {
      type: String,
      default: ''
    }
  },
  data () {
    return {
      color: '153, 153, 153',
      innerVal: '',
      mouseInfo: {
        clientY: 0,
        clientX: 0,
        space: 20,
        size: 100
      }
    }
  },
  computed: {
    pixPos () {
      const width = window.innerWidth
      const height = window.innerHeight
      let {clientY, clientX, space, size} = this.mouseInfo
      let left = clientX
      let top = clientY
      if ((clientY + size) > height) {
        top = clientY - size - space
      } else {
        top += space
      }
      if ((clientX + size) > width) {
        left = clientX - size - space
      } else {
        left += space
      }
      return `left: ${left}px; top:${top}px`
    }
  },
  methods: {
    close () {
      this.$emit('on-change', false)
    },
    initCanvas () {
      let oImg = this.$refs.img
      let canvas = this.draw(oImg)
      oImg.addEventListener('click', (e) => {
        const [r, g, b] = this.color.split(',')
        console.log({r, g, b})
        this.$emit('on-change', {r, g, b})
      })
      oImg.addEventListener('mousemove', (e) => {
        this.mouseInfo.clientY = e.clientY
        this.mouseInfo.clientX = e.clientX
        let x = e.offsetX
        let y = e.offsetY
        this.color = this.getPix(x, y, canvas.ctx)
      })
    },
    // 画图
    draw (img) {
      let style = window.getComputedStyle(img)
      let width = parseInt(style.width)
      let height = parseInt(style.height)
      img.style.width = width + 'px'
      img.style.height = height + 'px'
      img.style.maxWidth = width + 'px'
      img.style.maxHeight = height + 'px'
      let canvas = document.createElement('canvas')
      canvas.width = width
      canvas.height = height
      let ctx = canvas.getContext('2d')
      ctx.drawImage(img, 0, 0, width, height) // 这里一定要写上获取到图片的宽高,否则生成的图片和原图片大小不一样,吸取到的颜色不准
      return {
        ctx,
        canvas
      }
    },
    // 获取16进制颜色
    gethex (r, g, b) {
      r = r.toString(16)
      g = g.toString(16)
      b = b.toString(16)
      // 补0
      if (r.length === 1) r = '0' + r
      if (g.length === 1) g = '0' + g
      if (b.length === 1) b = '0' + b
      let hex = r + g + b
      // 简化处理,如 FFEEDD 可以写为 FED
      if (r.slice(0, 1) === r.slice(1, 1) && g.slice(0, 1) === g.slice(1, 1) && b.slice(0, 1) === b.slice(1, 1)) {
        hex = r.slice(0, 1) + g.slice(0, 1) + b.slice(0, 1)
      }
      return hex
    },
    // 获取像素颜色
    getPix (x, y, context) {
      const size = 10
      const num = this.boxSize / 2 / size // boxSize (必须是偶数 盒子大小) / 一半 / 每个像素大小
      x = x - num // 减掉自身像素个数的开始坐标
      y = y - num
      // 读取图片像素信息
      const w = num * 2 + 1 // 图片大小是 像素个数的2倍 (并多一行一列像素 为了视觉上的中心点 所以必须是奇数)
      const h = num * 2 + 1
      const centerPos = Math.ceil(w / 2) // 获取中心点坐标 向上取整
      let imageData = context.getImageData(x, y, w, h) // 截取 当前坐标下 w,h大小画图的数据
      const pos = this.getPos(imageData.data, w) // 计算当前截取画布下的像素信息(读取像素长度控制在千万以内 否则易导致浏览器崩溃)
      // 生成矩阵数据
      let arr = []
      Array(w).fill().map((item, index) => {
        let tx = index + 1
        const inners = Array(h).fill().map((item2, index2) => {
          let ty = index2 + 1
          const color = pos.get(`${tx},${ty}`)
          // 创建 10 * 10 px大小为单位的像素块
          arr.push(`<div data-set="${tx},${ty}" style="left:${index * 10}px; top:${index2 * 10}px; background: rgb(${color})"></div>`)
          return '#' + color
        })
        return inners
      })
      // 更新数据
      this.innerVal = arr.join('')
      // 返回当前中心坐标的 颜色值
      return pos.get(`${centerPos},${centerPos}`)
    },

    // 计算像素信息并返回
    getPos (data, imgWidth) {
      let pos = new Map()
      let length = data.length

      for (let i = 0; i < length; i++) {
        if (i % 4 === 0) { // 每四个元素为一个像素数据 r,g,b,alpha
          let x = i / 4 % imgWidth + 1 // 横坐标
          let y = Math.floor(i / 4 / imgWidth) + 1 // 纵坐标
          let alpha = Math.round(data[i + 3] / 255 * 100) / 100 // alpha 值

          if (data[i + 3] === 255) { // 没有alpha 值
            // let hex = this.gethex(data[i], data[i + 1], data[i + 2])
            let hex = `${data[i]}, ${data[i + 1]}, ${data[i + 2]}`
            pos.set(`${x},${y}`, hex)
          } else if (alpha > 0) { // 有alpha 值
            let rgba = `${data[i]}, ${data[i + 1]}, ${data[i + 2]}, ${alpha}`
            pos.set(`${x},${y}`, rgba)
          }
        }
      }
      return pos
    }
  }
}
</script>
<style lang='less' scoped>
.color-content {
  background-color: rgba(0 , 0, 0, 0.5);
  z-index: 9999;
  position: fixed;
  left: 0px;
  top: 0px;
  width: 100%;
  height: 100%;
  text-align: center;
  padding: 20px;
  .img-box {
    display: flex;
    width: 100%;
    height: 100%;
    justify-content: center;
    align-items: center;
  }
  img {
    cursor: crosshair;
    max-width: 100%;
    max-height: 100%;
  }
}
.close-icon {
  position: fixed;
  right: 10px;
  top: 10px;
  cursor: pointer;
  transition: ease-in-out 0.3s;
  color: #ebebeb;
  &:hover{
    color: #ff0000;
  }
}
</style>
<style lang="less">
  @pix-size: 10px;
 .pix-box {
    z-index: 999;
    position: absolute;
    // left: 20px;
    top: 30px;
    width: calc(var(--size) + @pix-size + 2px);
    height: calc(var(--size) + @pix-size + 2px);
    box-sizing: border-box;
    border: #fff solid 1px;
    border-radius: 50%;
    overflow: hidden;
    box-shadow: 0px 0px 5px #999;
    div.htmls {
      position: absolute;
      left: 0px;
      top: 0px;
      width: 100%;
      height: 100%;
      & > div {
        width: @pix-size;
        height: @pix-size;
        position: absolute;
        border: none;
      }
    }
    div.center {
      position: absolute;
      width: @pix-size;
      height: @pix-size;
      left: calc(var(--size) / 2);
      top: calc(var(--size) / 2);
      box-sizing: border-box;
      border: #999 solid 1px;
      z-index: 10;
      filter: invert(100%);
    }
    background: url('./images/bg-pixel.jpg')
  }

</style>

使用方法:

在某个页面中 引入  import canvasStraw from './canvas-straw.vue'  并注册组件

../index.vue 

<template>
  <div>
    <i-button @click="show = !show">打开</i-button>
    <canvas-straw src="http:../images/test.jpg" v-model="show"></canvas-straw>
  </div>
</template>
<script>
import canvasStraw from './canvas-straw.vue'
export default {
  components: {
    canvasStraw
  },
  data () {
    return {
      show: false
    }
  }
}
</script>

Logo

前往低代码交流专区

更多推荐