前段时间去客户现场,客户提出了一个“伽玛值”的需求,啊????伽玛值是什么。。。
后来看了客户原先系统的“伽玛值”处理效果,结果发现这不就是对图片的锐化值的调整嘛。那么问题来了,这怎么实现呢?属实没接触过啊。但是既然甲方粑粑提出来了,还能怎么办呢,只能硬着头皮写了,又是秃头的一天!!!
1、什么是伽玛值
在前端开发中,处理伽马值通常涉及到图像的显示和调整,特别是在图像渲染、显示优化和用户交互中。在Web开发中,处理伽马值的操作往往与图像的亮度、对比度以及如何让图像在不同显示设备上表现一致相关。
1.1 伽马值与图像显示
前端处理伽马值,通常是为了调整图像的显示效果,以便它能够在不同的显示设备上呈现出最佳效果。因为不同的显示器、操作系统和浏览器可能会有不同的色彩和亮度表现,前端开发人员通常需要在图像加载和渲染时对其进行调整。
1.2.伽马校正的应用
在前端开发中,伽马校正通常有以下几种使用场景:
-
图像加载和显示:在加载图像时,使用JavaScript对其进行伽马值调整,确保图像显示一致,尤其是当图像来源不同,或者浏览器渲染差异较大时。
-
Canvas绘图:在
<canvas>
元素中,伽马校正可能会涉及到像素级别的颜色调整,特别是当绘制图像或进行图像处理时。 -
色彩空间管理:不同的显示设备和Web环境可能使用不同的色彩空间(例如sRGB或Adobe RGB),前端开发人员可能需要考虑这些色彩空间之间的差异,并对图像或画面进行伽马校正,确保图像显示得尽可能真实。
2、实现代码
苦苦肝了一天,终于把功能实现了,我这里封装成了一个单独的组件,可以供之后调用,废话不多说,直接上代码。
<template>
<el-dialog title="伽马值调整"
:visible.sync="iamgeModel"
width="80%"
@open="onDialogOpen"
@close="onDialogClose">
<div>
<!-- <input type="file"
@change="onFileChange"
accept="image/*"> -->
<div v-if="imageSrc"
style="margin-top: 20px; text-align: center;">
<img :src="imageSrc"
alt="预览图"
ref="previewImage"
:style="imageStyle">
</div>
<el-slider v-model="gammaValue"
:min="0.1"
:max="3"
:step="0.01"
label="伽玛值"
@change="debouncedAdjustImageGamma" />
<div style="text-align: center; margin-top: 10px;">
<el-button type="primary"
@click="printImage">打印</el-button>
</div>
</div>
</el-dialog>
</template>
<script>
import { debounce } from 'lodash'
import { findByEfileIdBase64 } from '@/api/v2/tableData'
export default {
data () {
return {
iamgeModel: false,
originalImageSrc: null,
imageSrc: null,
gammaValue: 1,
imageStyle: {
maxWidth: '100%',
maxHeight: '80vh',
objectFit: 'contain'
},
worker: null, // Web Worker 存储变量
debouncedAdjustImageGamma: null, // 防抖函数
loadingInstance: null // 加载动画实例
}
},
methods: {
init (value) {
console.log('value :>> ', value)
// 获取图片文件base64
findByEfileIdBase64(value).then((result) => {
if (result.status != 0) {
this.$Message.error('该文件原始文件不是图片类型!')
return
}
console.log('result :>> ', result)
let base64String = result.data
const mimeType = 'image/png' // 根据实际文件类型设置 MIME 类型
const file = this.base64ToFile(base64String, 'image.png', mimeType)
let efileParams = {
base64Value: result,
fileObj: file
}
console.log('efileParams :>> ', efileParams)
this.onFileChange(efileParams)
})
},
// base64转换文件对象
base64ToFile (base64String, fileName, mimeType) {
// 可以直接解码整个 Base64 字符串
const byteCharacters = atob(base64String) // 解码 Base64 字符串
// 创建一个字节数组
const byteArrays = []
for (let offset = 0; offset < byteCharacters.length; offset++) {
const byte = byteCharacters.charCodeAt(offset)
byteArrays.push(byte)
}
// 创建一个 Blob 对象,指定 MIME 类型
const byteArray = new Uint8Array(byteArrays)
const blob = new Blob([byteArray], { type: mimeType })
// 返回一个 File 对象(可以指定文件名)
return new File([blob], fileName, { type: mimeType })
},
onDialogOpen () {
// 在对话框打开时初始化 Web Worker
const workerScript = `
self.onmessage = function(e) {
const { imageData, gammaValue } = e.data;
const data = imageData.data;
const gamma = gammaValue;
// 遍历每个像素,调整颜色值
for (let i = 0; i < data.length; i += 4) {
for (let j = 0; j < 3; j++) {
let colorValue = data[i + j];
colorValue = 255 * Math.pow(colorValue / 255, 1 / gamma);
data[i + j] = Math.min(255, Math.max(0, colorValue));
}
}
// 返回处理后的图像数据
self.postMessage({ imageData });
};
`
// 使用 Blob 动态创建 Worker
const blob = new Blob([workerScript], { type: 'application/javascript' })
this.worker = new Worker(URL.createObjectURL(blob))
this.worker.onmessage = (e) => {
// 从 Worker 中接收修改后的图像数据
const { imageData } = e.data
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
canvas.width = imageData.width
canvas.height = imageData.height
ctx.putImageData(imageData, 0, 0)
this.imageSrc = canvas.toDataURL() // 获取修改后的图片数据
// 完成后关闭加载动画
if (this.loadingInstance) {
this.loadingInstance.close()
}
}
// 防抖调整图像的函数
this.debouncedAdjustImageGamma = debounce(this.adjustImageGamma, 100) // 防抖:每100ms触发一次
},
onDialogClose () {
// 在对话框关闭时销毁 Web Worker 和防抖函数
if (this.worker) {
this.worker.terminate()
this.worker = null
}
if (this.loadingInstance) {
this.loadingInstance.close() // 关闭加载动画
}
},
onFileChange (event) {
const file = event.fileObj
console.log('file :>> ', file)
if (file && file.type.startsWith('image')) {
const reader = new FileReader()
console.log('reader :>> ', reader)
reader.onload = (e) => {
this.originalImageSrc = e.target.result// 需要一个 base64格式图片
this.imageSrc = this.originalImageSrc
this.adjustImageGamma() // 文件上传后立刻处理图像
}
reader.readAsDataURL(file)
}
},
adjustImageGamma () {
// 开始加载时显示 loading 动画
this.loadingInstance = this.$loading({
lock: true,
text: '处理中,请稍候...',
background: 'rgba(0, 0, 0, 0.7)'
})
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
const img = new Image()
img.src = this.originalImageSrc
img.onload = () => {
canvas.width = img.width
canvas.height = img.height
ctx.drawImage(img, 0, 0)
// 获取图像数据
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)
// 发送图像数据到 worker 进行处理
if (this.worker) {
this.worker.postMessage({ imageData, gammaValue: this.gammaValue })
}
}
},
printImage () {
const printWindow = window.open('', '_blank')
printWindow.document.write('<img src="' + this.imageSrc + '" />')
printWindow.print()
}
}
}
</script>