如何正确实现通过mix-blend-mode实现鼠标悬停波形时改变波形颜色的效果?
如何正确实现通过mix-blend-mode实现鼠标悬停波形时改变波形颜色的效果?
嘿,我看你在尝试用WaveSurfer做悬停变色的波形效果时遇到了混合模式导致的背景亮条问题,咱们来一步步解决它!
先说说你当前方案出问题的原因
你用了mix-blend-mode: plus-lighter,这个混合模式的逻辑是把两个元素的RGB值直接相加。你的hover层用了和页面背景一样的rgb(28,28,28),当它和背景叠加时,RGB值相加就变成了rgb(56,56,56),这就是那个讨厌的亮条的来源。而且这个模式会同时影响背景和波形,没法做到只改变波形颜色的效果。
推荐方案:直接用WaveSurfer的API控制颜色(最靠谱)
其实WaveSurfer本身就支持动态修改波形颜色,完全不需要额外的hover div和混合模式,这样既简单又不会有奇怪的叠加问题。
修改后的React组件代码:
import { FC, useEffect, useRef } from 'react'; import WaveSurfer from 'wavesurfer.js'; import { useStyles } from 'src/components/PageTrack/Waveform/style'; interface WaveformProps { audioUrl: string; } const SoundcloudWaveform: FC<WaveformProps> = ({ audioUrl }) => { const classes = useStyles(); // 用ref保存WaveSurfer实例,方便后续调用API const wavesurferRef = useRef<WaveSurfer | null>(null); useEffect(() => { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); // 原始波形渐变(和你原来的一致) const originalGradient = ctx!.createLinearGradient(0, 0, 0, canvas.height * 1.35); originalGradient.addColorStop(0, '#656666'); originalGradient.addColorStop((canvas.height * 0.7) / canvas.height, '#656666'); originalGradient.addColorStop((canvas.height * 0.7 + 1) / canvas.height, '#ffffff'); originalGradient.addColorStop((canvas.height * 0.7 + 2) / canvas.height, '#ffffff'); originalGradient.addColorStop((canvas.height * 0.7 + 3) / canvas.height, '#B1B1B1'); originalGradient.addColorStop(1, '#B1B1B1'); // 悬停时的高亮渐变(这里可以完全自定义你想要的亮色,我改成了全白示例) const hoverGradient = ctx!.createLinearGradient(0,0,0,canvas.height * 1.35); hoverGradient.addColorStop(0, '#ffffff'); hoverGradient.addColorStop((canvas.height * 0.7) / canvas.height,'#ffffff'); hoverGradient.addColorStop((canvas.height * 0.7 + 1) / canvas.height,'#ffffff'); hoverGradient.addColorStop((canvas.height * 0.7 + 2) / canvas.height,'#ffffff'); hoverGradient.addColorStop((canvas.height * 0.7 + 3) / canvas.height,'#ffffff'); hoverGradient.addColorStop(1, '#ffffff'); // 进度条渐变保持不变 const progressGradient = ctx!.createLinearGradient(0,0,0,canvas.height * 1.35); progressGradient.addColorStop(0, '#EE772F'); progressGradient.addColorStop((canvas.height * 0.7) / canvas.height,'#EB4926'); progressGradient.addColorStop((canvas.height * 0.7 + 1) / canvas.height,'#ffffff'); progressGradient.addColorStop((canvas.height * 0.7 + 2) / canvas.height,'#ffffff'); progressGradient.addColorStop((canvas.height * 0.7 + 3) / canvas.height,'#F6B094'); progressGradient.addColorStop(1, '#F6B094'); const wavesurfer = WaveSurfer.create({ container: '#waveform', waveColor: originalGradient, progressColor: progressGradient, barWidth: 3, url: audioUrl, dragToSeek: true, }); wavesurferRef.current = wavesurfer; wavesurfer.load(audioUrl); // 点击播放暂停逻辑不变 wavesurfer.on('interaction', () => { wavesurfer.playPause(); }); const waveform = document.querySelector('#waveform')!; // 鼠标进入时切换高亮颜色 const handleMouseEnter = () => { wavesurferRef.current?.setWaveColor(hoverGradient); }; // 鼠标离开时恢复原始颜色 const handleMouseLeave = () => { wavesurferRef.current?.setWaveColor(originalGradient); }; waveform.addEventListener('mouseenter', handleMouseEnter); waveform.addEventListener('mouseleave', handleMouseLeave); // 组件卸载时清理事件和实例 return () => { waveform.removeEventListener('mouseenter', handleMouseEnter); waveform.removeEventListener('mouseleave', handleMouseLeave); wavesurfer.destroy(); }; }, [audioUrl]); return ( <div className={classes.root}> <div id="waveform"></div> </div> ); }; export default SoundcloudWaveform;
修改后的CSS(去掉hover相关样式):
import { makeStyles } from '@material-ui/core'; export const useStyles = makeStyles( (theme) => ({ root: { position: 'relative', '& #waveform': { cursor: 'pointer', position: 'relative', }, }, }), { name: 'SoundCloudWaveform' }, );
这个方案的好处是直接调用WaveSurfer的setWaveColorAPI,完全避免了混合模式带来的兼容性和颜色计算问题,你可以精准控制悬停时的波形颜色,实现你想要的“只保留波形亮色”的效果。
如果你坚持要用混合模式的方案
如果一定要保留hover div的思路,那可以调整hover层的颜色和混合模式,让它只影响波形,不影响背景:
修改CSS中的#hover样式:
'& #hover': { position: 'absolute', left: 0, top: 0, zIndex: 10, pointerEvents: 'none', height: '100%', // 覆盖整个波形 width: '100%', // 或者保持你原来的跟随鼠标宽度逻辑 opacity: 0, transition: 'opacity 0.2s ease', background: '#000000', // 改成黑色 mixBlendMode: 'screen', // 改成screen混合模式 },
screen混合模式会让黑色和背景(rgb(28,28,28))叠加后保持背景色,不会出现亮条,而波形的颜色和黑色叠加后会变得更亮,这样就能实现只改变波形颜色的效果。不过这个方法在不同浏览器可能有细微差异,不如直接用WaveSurfer API稳定。
备注:内容来源于stack exchange,提问作者artyom.poteshkin98
相关产品推荐
相关产品推荐

