1. 程式人生 > 實用技巧 >線性插值編寫視覺化程式碼

線性插值編寫視覺化程式碼

線性插值

處於創意編碼,遊戲開發,資料視覺化和生成創意的功能

[start,end] 在範圍內插值,t通常在[0,1] 範圍內

function lerp (start, end, t) {
  return start * (1 - t) + end * t;
}
//雖然這樣,但是我感覺現在這樣,更容易理解
start+(end-start)*t 
// 當 t 為 50%的時候,結果為50
lerp(0, 100, 0.5); // 50  
// 當 t 為 0% 的時候,結果20
lerp(20, 80, 0); // 20
// 當t 為1 的時候,結果為5
lerp(30, 5, 1); // 5
lerp(-1, 1, 0.5); // 0
lerp(0.5, 1, 0.5); // 0.75

案例

lerp(20, 50, t)逐漸增大圓的半徑或lerp(20, 10, t)逐漸減小其線的粗細

關鍵程式碼

      // 半徑20-50
      const radius = lerp(20, 50, t);

      // 線寬 20-10
      const lineWidth = lerp(20, 10, t);
  const canvasSketch = require('canvas-sketch');
  function lerp (start, end, t) {
    return start * (1 - t) + end * t;
  }

  const settings = {
    dimensions: [ 512, 512 ],
    animate: true,
    duration: 5
  };

  const rect = (context, x, y, width, height, color) => {
    context.fillStyle = color;
    context.fillRect(x, y, width, height);
  };

  const circle = (context, x, y, radius, color, lineWidth) => {
    context.strokeStyle = context.fillStyle = color;
    context.beginPath();
    context.arc(x, y, radius, 0, Math.PI * 2, false);
    context.lineWidth = lineWidth;
    if (lineWidth != null) context.stroke();
    else context.fill();
  };

  const progress = (context, time, width, height, margin = 0) => {
    context.fillStyle = 'white';
    context.fillRect(
      margin * 2,
      height - margin * 2,
      (width - margin * 4) * time,
      4
    );
  };

  const sketch = ({ width, height }) => {
    const margin = 25;

    return props => {
      // 拿到我們需要的屬性
      const { context, width, height, playhead } = props;


      // 設定距離margin的背景
      rect(context, margin, margin, width - margin * 2, height - margin * 2, '#e5b5b5');

      // 畫出場景
      draw(props);

      // 在下面畫一個時間軸
      progress(context, playhead, width, height, margin);
    };

    function draw ({ context, width, height, playhead, deltaTime }) {
      // Get our 0..1 range value
      const t = playhead;

      // 半徑20-50
      const radius = lerp(20, 50, t);

      // 線寬 20-10
      const lineWidth = lerp(20, 10, t);

      // 畫圓
      circle(context, width / 2, height / 2, radius, 'white', lineWidth);
    }
  };

  canvasSketch(sketch, settings);

參考資料

逆線性插值

我們需要20過渡到40的比例是50%,值設定了30

假設您要對0到100畫素之間的滾動位置做出反應。我們稱它們為ab。用下面的逆線性插值,你在傳遞ab以及滾動位置v

function invlerp(a, b, v) {
  return ( v - a ) / ( b - a )
}

但是我們會發現返回的值可能會大於1,超過兩個極限

const lerp = (x, y, a) => x * (1 - a) + y * a
const invlerp = (a, b, v) => clamp(( v - a ) / ( b - a ))
const clamp = (v, min = 0, max = 1) => Math.min(max, Math.max(min, v))

我自己也是很矇蔽,直接上案例

invlerp(20, 40, 20) // 0
invlerp(20, 40, 30) // 0.5

結果範圍20-40結果返回的是0-1

在小於等於20的時候都是0

大於等於40的時候都是1

demo