You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何正确实现通过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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.20 08:54:36