1. 背景
为了让本公司的定制app网站更加具有样式多样性,提出了新增渐变模式,其中就涉及到了图片的渐变,同样也可以设置图片渐变的透明度
2. 渐变核心思路
- 将图片绘制到画布上
- 获取画布上的图像像素
- 把hex格式的颜色转成rgba格式的颜色
- 根据两种颜色获取到颜色渐变列表
- 再根据方向给每一个像素点赋值
- 最后将画布导出为png格式的图片地址
3. 代码实现
1. 封装一个方法用于hex转成rgba
export const hexToRgba = (hex) => {
const rgb = []
for (let i = 1; i < hex.length; i += 2) {
rgb.push(parseInt('0x' + hex.slice(i, i + 2)))
}
return rgb
}
2. 封装一个方法用于获取渐变色列表
import { hexToRgba } from "@/utils/icon-gradient/hexToRgba";
export const getGradientList = (startColor, endColor, step) => {
const sColor = hexToRgba(startColor)
const eColor = hexToRgba(endColor)
const rStep = (eColor[0] - sColor[0]) / (step - 1)
const gStep = (eColor[1] - sColor[1]) / (step - 1)
const bStep = (eColor[2] - sColor[2]) / (step - 1)
let aStep
// 判断颜色有没有透明度
if (eColor[3] && sColor[3]) {
aStep = (eColor[3] - sColor[3]) / (step - 1)
}
const gradientColorsList = []
for (let i = 0; i < step; i++) {
if (aStep) {
gradientColorsList.push(new Array(Math.round(rStep * i + sColor[0]), Math.round(gStep * i + sColor[1]), Math.round(bStep * i + sColor[2]), Math.round(aStep * i + sColor[3])))
} else {
gradientColorsList.push(new Array(Math.round(rStep * i + sColor[0]), Math.round(gStep * i + sColor[1]), Math.round(bStep * i + sColor[2])))
}
}
return gradientColorsList
}
3. 封装一个方法用于获取渐变后的图片地址
import { getGradientList } from "@/utils/icon-gradient/getGradientList";
export const getImageUrl = (canvasDom, imageUrl, gradientColors, callback) => {
const startColor = gradientColors.colors[0]
const endColor = gradientColors.colors[1]
const angle = gradientColors.angle
const ctx = canvasDom.getContext('2d', { willReadFrequently: true })
const w = canvasDom.width
const h = canvasDom.height
ctx.clearRect(0, 0, w, h)
const image = new Image()
image.src = imageUrl
image.crossOrigin = "Anonymous";
image.onload = () => {
// 将图片绘制到画布上
ctx.drawImage(image, 0, 0, w, h)
// 获取画布上的图像像素
const imgData = ctx.getImageData(0, 0, w, h)
let gradientList
if (angle == 0) {
// 从下到上
gradientList = getGradientList(startColor, endColor, h)
for (let i = 0; i < w; i++) {
for (let j = 0; j < h; j++) {
imgData.data[((i * (imgData.width * 4)) + (j * 4)) + 0] = gradientList[gradientList.length - 1 - i][0]
imgData.data[((i * (imgData.width * 4)) + (j * 4)) + 1] = gradientList[gradientList.length - 1 - i][1]
imgData.data[((i * (imgData.width * 4)) + (j * 4)) + 2] = gradientList[gradientList.length - 1 - i][2]
if (gradientList[gradientList.length - 1 - i][3]) {
imgData.data[((i * (imgData.width * 4)) + (j * 4)) + 3] = Math.round(imgData.data[((i * (imgData.width * 4)) + (j * 4)) + 3] * gradientList[gradientList.length - 1 - i][3] / 255)
}
}
}
} else if (angle == 90) {
// 从左到右
gradientList = getGradientList(startColor, endColor, w)
for (let i = 0; i < h; i++) {
for (let j = w - 1; j >= 0; j--) {
imgData.data[((j * (imgData.width * 4)) + (i * 4)) + 0] = gradientList[i][0]
imgData.data[((j * (imgData.width * 4)) + (i * 4)) + 1] = gradientList[i][1]
imgData.data[((j * (imgData.width * 4)) + (i * 4)) + 2] = gradientList[i][2]
if (gradientList[i][3]) {
imgData.data[((j * (imgData.width * 4)) + (i * 4)) + 3] = Math.round(imgData.data[((j * (imgData.width * 4)) + (i * 4)) + 3] * gradientList[i][3] / 255)
}
}
}
}
// 将修改后的代码复制回画布中
ctx.putImageData(imgData, 0, 0)
// 图片导出为 png 格式
const imgType = "image/png"
canvasDom.toBlob((blob) => {
callback(URL.createObjectURL(blob))
}, imgType, 1)
}
}
注意:如果图片的地址是远程地址,那么就会报一个跨域的错误,需要后端把标头改成*