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

JavaScript如何将线性渐变函数改写为贝塞尔平滑渐变函数

JS实现贝塞尔缓动渐变方案

现有实现说明

你当前编写的线性渐变生成器运行逻辑通顺,已实现黑色到绿色的均匀线性过渡效果,核心插值逻辑为:

  • 计算输入值a(起点值)、b(终点值)的差值
  • 根据传入的0-1区间比例参数decimal计算线性插值:decimal=0.5时返回两值中点,decimal=0.3时返回更靠近起点a的对应值
    现有完整代码如下:
// Gradient Function
function linearGradient(a, b, decimal) {
    // find distance between a and b
    const dist = b - a

    /* if you multiply the distance by
    the decimal and add the minimum
    again, it gives you the point at
    which the decimal interacts with
    the number */
    return a + dist * decimal
}

/* ------- EXAMPLE USE ------- */

// Green & Black colour definition
const color_black = [0, 0, 0]
const color_green = [0, 255, 0]

// Make a for-loop to find the Red, Green and Blue values of each stage from black to green
const resolution = 20
for (let i = 0; i < resolution; i ++) {
    red = linearGradient(color_black[0], color_green[0], i / resolution)
    green = linearGradient(color_black[1], color_green[1], i / resolution)
    blue = linearGradient(color_black[2], color_green[2], i / resolution)
}

当前线性渐变的插值沿直线匀速从起点过渡到终点,视觉效果偏生硬。

改造思路

你需要的「起步逐步加速、到达中点后逐步减速」的自然过渡效果,本质是缓入缓出(ease-in-out)贝塞尔曲线,不需要改动原有线性插值的核心逻辑,只需要新增一步比例映射:

  1. 保留原有linearGradient线性插值函数完全不变
  2. 新增贝塞尔缓动函数,把原始的均匀比例值(即循环里的i/resolution)映射为符合贝塞尔变化规律的新比例值
  3. 把映射后的新比例值传入linearGradient计算颜色通道值即可

具体实现

轻量版:二次方缓入缓出

如果只需要标准的平滑S型过渡,不需要自定义曲线弧度,直接用计算量极小的二次方缓动公式即可,效果完全匹配需求:

// 新增:二次方缓入缓出贝塞尔映射
function easeInOutQuad(t) {
    return t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2
}

// 原有线性插值函数保持不变
function linearGradient(a, b, decimal) {
    const dist = b - a
    return a + dist * decimal
}

/* ------- 调用示例 ------- */
const color_black = [0, 0, 0]
const color_green = [0, 255, 0]
const resolution = 20
for (let i = 0; i < resolution; i ++) {
    // 先对原始比例做贝塞尔缓动映射
    const ratio = easeInOutQuad(i / resolution)
    // 再用映射后的比例计算颜色值
    const red = linearGradient(color_black[0], color_green[0], ratio)
    const green = linearGradient(color_black[1], color_green[1], ratio)
    const blue = linearGradient(color_black[2], color_green[2], ratio)
    // 后续使用red/green/blue绘制即可
}

可调版:自定义三次贝塞尔

如果需要灵活调整过渡的加速、减速节奏(比如让起步更快、收尾拖尾更长),可以用通用三次贝塞尔实现,和CSS的cubic-bezier参数规则完全一致:

// 新增:三次贝塞尔缓动映射,参数对应CSS cubic-bezier的4个控制点值
function cubicBezier(p1x, p1y, p2x, p2y) {
    const NEWTON_ITERATIONS = 4
    const NEWTON_MIN_SLOPE = 0.001
    const SUBDIVISION_PRECISION = 0.0000001
    const SUBDIVISION_MAX_ITERATIONS = 10

    function A(aA1, aA2) { return 1.0 - 3.0 * aA2 + 3.0 * aA1 }
    function B(aA1, aA2) { return 3.0 * aA2 - 6.0 * aA1 }
    function C(aA1) { return 3.0 * aA1 }

    function calcBezier(aT, aA1, aA2) {
        return ((A(aA1, aA2) * aT + B(aA1, aA2)) * aT + C(aA1)) * aT
    }
    function getSlope(aT, aA1, aA2) {
        return 3.0 * A(aA1, aA2) * aT * aT + 2.0 * B(aA1, aA2) * aT + C(aA1)
    }

    function binarySubdivide(aX, aA, aB, mX1, mX2) {
        let currentX, currentT, i = 0
        do {
            currentT = aA + (aB - aA) / 2.0
            currentX = calcBezier(currentT, mX1, mX2) - aX
            if (currentX > 0) aB = currentT
            else aA = currentT
        } while (Math.abs(currentX) > SUBDIVISION_PRECISION && ++i < SUBDIVISION_MAX_ITERATIONS)
        return currentT
    }

    function newtonRaphsonIterate(aX, aGuessT, mX1, mX2) {
        for (let i = 0; i < NEWTON_ITERATIONS; ++i) {
            const currentSlope = getSlope(aGuessT, mX1, mX2)
            if (currentSlope === 0.0) return aGuessT
            const currentX = calcBezier(aGuessT, mX1, mX2) - aX
            aGuessT -= currentX / currentSlope
        }
        return aGuessT
    }

    const _p1x = p1x, _p1y = p1y, _p2x = p2x, _p2y = p2y
    return function(t) {
        if (t === 0 || t === 1) return t
        const currentSlope = getSlope(t, _p1x, _p2x)
        if (currentSlope !== 0 && currentSlope >= NEWTON_MIN_SLOPE) {
            let t2 = newtonRaphsonIterate(t, t, _p1x, _p2x)
            return calcBezier(t2, _p1y, _p2y)
        } else if (currentSlope === 0) {
            return calcBezier(t, _p1y, _p2y)
        } else {
            let t2 = binarySubdivide(t, 0, 1, _p1x, _p2x)
            return calcBezier(t2, _p1y, _p2y)
        }
    }
}

// 初始化标准ease缓动曲线,对应控制点(0.25, 0.1, 0.25, 1),和你要的加速减速规律完全匹配
const ease = cubicBezier(0.25, 0.1, 0.25, 1)

// 原有线性插值函数保持不变
function linearGradient(a, b, decimal) {
    const dist = b - a
    return a + dist * decimal
}

/* ------- 调用示例 ------- */
const color_black = [0, 0, 0]
const color_green = [0, 255, 0]
const resolution = 20
for (let i = 0; i < resolution; i ++) {
    // 用自定义贝塞尔曲线映射比例
    const ratio = ease(i / resolution)
    const red = linearGradient(color_black[0], color_green[0], ratio)
    const green = linearGradient(color_black[1], color_green[1], ratio)
    const blue = linearGradient(color_black[2], color_green[2], ratio)
}

你可以通过修改cubicBezier的四个控制点参数调整过渡手感:

  • 前两个参数控制起步阶段的曲线弧度,数值越小起步加速越平缓
  • 后两个参数控制收尾阶段的曲线弧度,数值越大收尾减速越柔和

内容的提问来源于stack exchange,提问作者Joachim

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.02 03:24:28