svg dna解锁手机动画

代码语言:html

所属分类:动画

代码描述:svg dna解锁手机动画

代码标签: 手机动画

下面为部分代码预览,完整代码请点击下载或在bfwstudio webide中打开


<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">

<style>
body {
    margin: 0px;
    display: flex;
    justify-content: center;
    align-items: center;
    min-height: 100vh;
    width: 100vw;
    overflow: hidden;
}

.main-canvas {
    /* border: 1px dashed blueviolet; */
    width: 200px;
    height: 350px;
}
.social-wrapper {
    opacity: 0.7;
}

.social-wrapper svg g path {
    stroke: rgb(177, 177, 177) !important;
}
.social-wrapper svg g rect {
    stroke: rgb(177, 177, 177) !important;
}
.social-wrapper svg g circle {
    stroke: rgb(177, 177, 177) !important;
}

.socials-container {
    padding: 4px;
    position: fixed;
    top: 0;
    left: 0;
    width: 20px;
}
</style>

</head>
<body translate="no">

<canvas class="main-canvas" width="1000" height="700"></canvas>
<script>
// svgs
const fingerprintSvg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 400"><title>fingerprint</title><g id="finger-print"><path d="M160.38,222.11s12,84.05-46.83,130.88" style="fill:none;stroke:#000;stroke-miterlimit:10;stroke-width:5px"/><path d="M158,200.5s6-38.43,43.22-37.23,42,37.23,42,37.23,3.6,32.42,3.6,45.62" style="fill:none;stroke:#000;stroke-miterlimit:10;stroke-width:5px"/><path d="M178.39,280.94s6.23-46.79,1.2-74.44c-4.8-26.42,37.22-31.22,40.82-6s20.41,90.05-42,175.3" style="fill:none;stroke:#000;stroke-miterlimit:10;stroke-width:5px"/><path d="M200,205.3S225.21,293,155.57,372.2" style="fill:none;stroke:#000;stroke-miterlimit:10;stroke-width:5px"/><path d="M204.8,378.2S242,321.77,245.63,266.54" style="fill:none;stroke:#000;stroke-miterlimit:10;stroke-width:5px"/><path d="M131.56,365s33.62-30,40.82-62.43" style="fill:none;stroke:#000;stroke-miterlimit:10;stroke-width:5px"/><path d="M94.34,342.18s30-25.22,39.62-54a140.18,140.18,0,0,0,6-62.44c-1.2-9.6-7.2-43.22,15.61-63.64s39.63-19.21,51.63-18" style="fill:none;stroke:#000;stroke-miterlimit:10;stroke-width:5px"/><path d="M227.62,148.87s90.05,40.82,3.6,226.93" style="fill:none;stroke:#000;stroke-miterlimit:10;stroke-width:5px"/><path d="M258.83,367.39s20.42-37.22,21.62-56.43" style="fill:none;stroke:#000;stroke-miterlimit:10;stroke-width:5px"/><path d="M285.25,290.55s10.81-57.63-2.4-102.06C257.63,84,105.14,111.64,117.15,213.7c4.8,31.22,2.4,37.23,1.2,43.23" style="fill:none;stroke:#000;stroke-miterlimit:10;stroke-width:5px"/><path d="M77.53,329s33.62-28.82,36-49.23" style="fill:none;stroke:#000;stroke-miterlimit:10;stroke-width:5px"/><path d="M64.32,311s28.82-20.41,31.22-46.83S93,219,94.34,198.1c1.2-19.22,10.8-50.43,39.62-75.65" style="fill:none;stroke:#000;stroke-miterlimit:10;stroke-width:5px"/><path d="M153.17,111.64s62.44-32.41,116.47,14.41" style="fill:none;stroke:#000;stroke-miterlimit:10;stroke-width:5px"/><path d="M284.05,141.66s56.43,66,4.8,211.33" style="fill:none;stroke:#000;stroke-miterlimit:10;stroke-width:5px"/><path d="M52.31,295.35s36-30,24-67.24c-4.81-10.8-3.6-19.21-3.6-19.21" style="fill:none;stroke:#000;stroke-miterlimit:10;stroke-width:5px"/><path d="M77.53,188.49C107.55,36,310.46,46.81,327.27,199.3" style="fill:none;stroke:#000;stroke-miterlimit:10;stroke-width:5px"/><path d="M329.68,222.11S336.88,287,318.87,329" style="fill:none;stroke:#000;stroke-miterlimit:10;stroke-width:5px"/><path d="M242.76,65.48c71.31,21.73,130.11,96.81,107.33,222.67" style="fill:none;stroke:#000;stroke-miterlimit:10;stroke-width:5px"/><path d="M42.71,276.14s16.81-12,12-44.43c-4.8-14.4-1.2-38.42-1.2-38.42,14.39-100.7,94.14-144,168-132.66" style="fill:none;stroke:#000;stroke-miterlimit:10;stroke-width:5px"/><path d="M298.91,66.41A170.26,170.26,0,0,1,370.5,205.3c0,1.83,0,3.65-.09,5.47" style="fill:none;stroke:#000;stroke-miterlimit:10;stroke-width:5px"/><path d="M61,106.55A170.64,170.64,0,0,1,276.52,52.89" style="fill:none;stroke:#000;stroke-miterlimit:10;stroke-width:5px"/><path d="M36,252.13A171.12,171.12,0,0,1,49,126.05" style="fill:none;stroke:#000;stroke-miterlimit:10;stroke-width:5px"/></g></svg>`;
const phoneSvg = `<svg id="phone" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 683 1333"><title>phone-screen</title><path id="outer-path" d="M111.31,1309.27c-50.43,0-91.31-42.08-91.31-94L19.69,114.5c0-51.91,40.89-94,91.32-94H565c50.43,0,91.31,42.09,91.31,94L656,1215.27c0,51.92-40.89,94-91.32,94Z" style="fill:none;stroke:#000;stroke-miterlimit:10;stroke-width:4px"/><path d="M619,115.5c0-.17,0-.33,0-.5,0-31.2-26-56.5-58-56.5H508s-7,1-10,4-3,4-3,10-9,32-30,32H212c-21,0-30-26-30-32s0-7-3-10-10-4-10-4H119c-32,0-58,25.3-58,56.5,0,.17,0,.33,0,.5v1102c0,.17,0,.33,0,.5,0,31.2,26,56.5,58,56.5H561c32,0,58-25.3,58-56.5,0-.17,0-.33,0-.5Z" style="fill:none;stroke:#000;stroke-miterlimit:10;stroke-width:4px"/><circle cx="408" cy="73.5" r="11" style="fill:none;stroke:#000;stroke-miterlimit:10;stroke-width:4px"/><rect x="300" y="68.5" width="80" height="11" rx="5.5" style="fill:none;stroke:#000;stroke-miterlimit:10;stroke-width:4px"/></svg>`;
const fingerASvg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 418 865"><title>finger-a</title><g id="finger-a"><path d="M192.33,238.36s29.38,17.34,68.57,8.09" style="fill:none;stroke:#000;stroke-miterlimit:10;stroke-width:4px"/><path d="M192.33,105.42s10.88,25.43,39.18,26.59S274,100.8,275,93.86s2.18-16.18,2.18-16.18S259.81,53.4,231.51,54.56s-37,20.8-37,20.8Z" style="fill:none;stroke:#000;stroke-miterlimit:10;stroke-width:4px"/><path d="M158.58,475.34s55.51,1.15,71.84,13.87,35.92-13.87,35.92-13.87" style="fill:none;stroke:#000;stroke-miterlimit:10;stroke-width:4px"/><path d="M188,423.32s23.95-1.16,31.57,15" style="fill:none;stroke:#000;stroke-miterlimit:10;stroke-width:4px"/><path d="M255.45,461.47s-45.71-12.72-52.24-5.78" style="fill:none;stroke:#000;stroke-miterlimit:10;stroke-width:4px"/><path d="M211.92,391s43.53,3.47,49,43.93" style="fill:none;stroke:#000;stroke-miterlimit:10;stroke-width:4px"/><path d="M255,808.5l12.43-97.34s35.91-228.89,35.91-271.66S299,141.26,296.81,119.29,283.75,41.84,238,36.06s-55.51,34.68-57.69,55.49-17.41,209.23-18.5,230S140.08,433.72,139,460.31s-27.21,150.28-44.62,178" style="fill:none;stroke:#000;stroke-miterlimit:10;stroke-width:4px"/></g></svg>`;
const fingerBSvg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 418 865"><title>finger-b</title><g id="finger-b"><path d="M228.5,851.5s29-194,42-236,48-210,51-258,22-227,7-262-42-49-59-46-33,18-33,18l-32,134s-4,29-6,50-40,141-40,141-20,35-27,67-48,173-70,219" style="fill:none;stroke:#000;stroke-miterlimit:10;stroke-width:4px"/><path d="M197.5,251.5s26,6,35,0" style="fill:none;stroke:#000;stroke-miterlimit:10;stroke-width:4px"/><path d="M146.5,485.5s47,32,66,13" style="fill:none;stroke:#000;stroke-miterlimit:10;stroke-width:4px"/><path d="M164.5,455.5s25,27,51,14" style="fill:none;stroke:#000;stroke-miterlimit:10;stroke-width:4px"/><path d="M172.5,422.5s-23,20-19,45" style="fill:none;stroke:#000;stroke-miterlimit:10;stroke-width:4px"/><path d="M242.5,62.57s37,.93,52,26.93l-5,23s-11,24-40,24S221,132.37,221,132.37" style="fill:none;stroke:#000;stroke-miterlimit:10;stroke-width:4px"/></g></svg>`;
const fingerCSvg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 418 865"><title>finger-c</title><g id="finger-c"><path d="M186.14,851.73S231.86,700,232.83,692.2,350.54,375.08,351.51,291.42c0,0,45.72-122.57,37-180.93s-34.05-60.31-34.05-60.31S321.36,40.45,312.6,55s-28.21,73-28.21,73-40.85,107-39.88,113.81c0,0-52.53,89.5-67.12,136.19,0,0-21.4,25.29-30.16,42.8s-13.62,39.88-13.62,43.77S45.09,666.91,28.55,682.47" style="fill:none;stroke:#000;stroke-miterlimit:10;stroke-width:4px"/><path d="M138.48,469.44s20.42,19.45,54.47,8.75" style="fill:none;stroke:#000;stroke-miterlimit:10;stroke-width:4px"/><path d="M160.85,446.09s10.7,11.67,23.35,11.67" style="fill:none;stroke:#000;stroke-miterlimit:10;stroke-width:4px"/><path d="M172.52,404.26s-21.4,33.08-20.42,53.5" style="fill:none;stroke:#000;stroke-miterlimit:10;stroke-width:4px"/><path d="M250.34,242.78s18.49-2.91,26.27,1.95" style="fill:none;stroke:#000;stroke-miterlimit:10;stroke-width:4px"/><path d="M284.39,128s49.61,1,64.2-41.83c0,0-9.83-24.74-36-27.93" style="fill:none;stroke:#000;stroke-miterlimit:10;stroke-width:4px"/></g></svg>`;
const lockSvg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 400"><title>lock</title><g id="lock"><path d="M299,317c0,36-30,33-30,33H124c-26,0-25-33-25-33V217s-1-33,25-33H269s30-3,30,33"/><path d="M200,65a79,79,0,0,0-79,79v64h29V144a50,50,0,0,1,100,0V313h29V144A79,79,0,0,0,200,65Z"/></g></svg>`;

// colors
const lightColors = {
  bg: "white",
  outline: "black",
  fingerprint: "black" };


const darkColors = {
  bg: "black",
  outline: "white",
  fingerprint: "red" };


const canvas = document.querySelector(".main-canvas");
const canvas2 = document.createElement("canvas");
const ctx = canvas.getContext("2d");
const ctx2 = canvas2.getContext("2d");

function main() {
  let resp = computeRes();
  const glitchManager = new GlitchManager(resp);
  let textManager = new TextManager(resp, ["Setup DNA Unlock"]);

  let stateIndex = 0;
  const createState = (id, time) => ({ id, time });
  const states = [
  createState("fadein", 2500),
  createState("1stinstruction", 500),
  createState("finger-hold", 2000),
  createState("finger-off", 500),
  createState("needles", 3600),
  createState("glitch-beginning", 250),
  createState("demonic-scrawl", 6000),
  createState("lock", 500),
  createState("unlock", 400),
  createState("again?", 4000)];


  function onScreenChange() {
    Object.assign(resp, computeRes());
    canvas.style.width = resp.styleSize.w + "px";
    canvas.style.height = resp.styleSize.h + "px";
    canvas.width = resp.rasterSize.w;
    canvas.height = resp.rasterSize.h;
    canvas2.width = resp.rasterSize.w;
    canvas2.height = resp.rasterSize.h;
  }

  onScreenChange();
  window.onresize = onScreenChange;

  const phonePaths = svgStringToPathList(phoneSvg);
  const lockPaths = svgStringToPathList(lockSvg).map((x) =>
  getPathPoints(x).map(toLV3));

  const fingerprintPaths = svgStringToPathList(fingerprintSvg).map((x) =>
  getPathPoints(x).map(toLV3));

  const phoneFrontPoints = getPathPoints(phonePaths[0]).map(toLV3);
  const phoneBackPoints = getPathPoints(phonePaths[0]).map(
  x => new LV3(x.x, x.y, -30));

  const phoneDisplayPoints = getPathPoints(phonePaths[1]).map(toLV3);
  const phoneCenter = getPhoneCenter(phoneFrontPoints);

  const fingerAnim = new FingerAnimator(
  fingerASvg,
  fingerBSvg,
  fingerCSvg,
  resp);


  const phoneParts = {
    phoneCenter,
    phoneBackPoints,
    phoneDisplayPoints,
    phoneFrontPoints };


  let lines = Line.createInitialList();
  const needles = Needle.createInitialList();

  // lock colors
  const lockBlackColor = new LV3(0, 0, 0);
  const greenHex = hex2RGB("#17AB69");
  const lockGreenColor = new LV3(greenHex.r, greenHex.g, greenHex.b);

  let time = 0;
  let prevDelta = 0;
  let lastBG = "white";
  const body = document.body;
  function renderFunction(delta) {
    const dt = delta - prevDelta;
    time += dt;
    const state = states[stateIndex];

    let colors = state.id === "demonic-scrawl" ? darkColors : lightColors;
    if (state.id === "glitch-beginning") {
      colors = Math.random() < 0.5 ? darkColors : lightColors;
    }
    if (colors.bg !== lastBG) {
      lastBG = colors.bg;
      body.style.backgroundColor = colors.bg;
    }

    const T = Math.min(time / state.time, 1);

    let firstCtx = ctx;
    if (state.id === "glitch-beginning" || state.id === "demonic-scrawl") {
      firstCtx = ctx2;
    }

    firstCtx.fillStyle = colors.bg;
    firstCtx.fillRect(0, 0, resp.rasterSize.w, resp.rasterSize.h);
    firstCtx.fillStyle = colors.outline;
    // firstCtx.font = '50px serif'
    // firstCtx.fillText(`${Math.floor(time)}`, 30, 60);
    // firstCtx.fillText(`${state.id}`, 30, 120);
    // firstCtx.fillText(`${T}`, 30, 180);

    // each state
    switch (state.id) {
      case "fadein":
        const clrs = Object.assign({}, colors, {
          outline: `rgba(0, 0, 0, ${T})` });

        drawPhone(resp, ctx, 0, clrs, phoneParts);
        textManager.draw(ctx, T, clrs);
        break;
      case "1stinstruction":
        drawPhone(resp, ctx, 0, colors, phoneParts);
        drawFinger(resp, ctx, T, colors, fingerAnim);
        textManager.draw(ctx, T, colors);
        break;
      case "lock":
        drawPhone(resp, ctx, 0, colors, phoneParts);
        drawLock(lockPaths, resp, ctx, 0, 0, "black");
        break;
      case "unlock":
        {
          const t = Math.floor(T * 10) / 10;
          const lockColor = lockGreenColor.sub(lockBlackColor).scale(t);
          lockColor.x = Math.floor(lockColor.x);
          lockColor.y = Math.floor(lockColor.y);
          lockColor.z = Math.floor(lockColor.z);
          drawPhone(resp, ctx, 0, colors, phoneParts);
          drawLock(
          lockPaths,
          resp,
          ctx,
          t,
          0,
          `rgb(${lockColor.x}, ${lockColor.y}, ${lockColor.z})`);

        }
        break;
      case "again?":
        drawPhone(resp, ctx, 0, colors, phoneParts);
        drawLock(
        lockPaths,
        resp,
        ctx,
        1,
        0,
        `rgb(${lockGreenColor.x}, ${lockGreenColor.y}, ${lockGreenColor.z})`);

        textManager.draw(ctx, T, colors);
        break;
      case "finger-hold":
        drawPhone(resp, ctx, 0, colors, phoneParts);
        drawFinger(
        resp,
        ctx,
        1,
        Object.assign({}, colors, { outline: `rgba(0, 0, 0, ${0.3})` }),
        fingerAnim);

        drawFingerprint(resp, ctx, colors, 0, fingerprintPaths);
        textManager.draw(ctx, T, colors);
        break;
      case "finger-off":
        drawPhone(resp, ctx, T * -60, colors, phoneParts);
        drawFinger(
        resp,
        ctx,
        1 - T,
        Object.assign({}, colors, { outline: `rgba(0, 0, 0, ${0.3})` }),
        fingerAnim);

        drawFingerprint(resp, ctx, colors, T * -60, fingerprintPaths);
        drawNeedles(needles, resp, ctx, T * -60, dt);
        break;
      case "needles":
        drawPhone(resp, ctx, -60, colors, phoneParts);
        drawFingerprint(
        resp,
        ctx,
        Object.assign({}, colors, { fingerprint: `rgba(50, 50, 50, 0.3)` }),
        -60,
        fingerprintPaths);

        drawNeedles(needles, resp, ctx, -60, dt);
        drawFinger(resp, ctx, 0, colors, fingerAnim);
        textManager.draw(ctx, T, colors);
        break;
      case "glitch-beginning":
        glitchManager.N = 4;
        glitchManager.refreshRate = 200;
        glitchManager.tick(dt);
        drawPhone(resp, ctx2, -60, colors, phoneParts);
        drawFingerprint(resp, ctx2, colors, -60, fingerprintPaths);
        drawNeedles(needles, resp, ctx2, -60, dt);
        drawGlitchedScreen(glitchManager, resp, canvas2, ctx, colors);
        break;
      case "demonic-scrawl":
        glitchManager.N = 12;
        glitchManager.refreshRate = 100;
        glitchManager.tick(dt);
        const angle = -60 + T * 60;
        drawPhone(resp, ctx2, angle, colors, phoneParts);
        drawFingerprint(resp, ctx2, colors, angle, fingerprintPaths);
        drawNeedles(needles, resp, ctx2, angle, dt);
        drawLines(lines, resp, ctx2, angle, dt);
        textManager.draw(ctx2, T, colors);
        drawGlitchedScreen(glitchManager, resp, canvas2, ctx, colors);
        break;}


    prevDelta = delta;

    if (time > state.time) {
      stateIndex = (stateIndex + 1) % states.length;
      switch (states[stateIndex].id) {
        case "demonic-scrawl":
          lines = Line.createInitialList();
          textManager.textList = [
          "Your privacy is",
          "our priority"];

          break;
        case "needles":
          textManager.textList = [`Your DNA is then`, "extracted and analysed"];
          break;
        case "1stinstruction":
        case "finger-hold":
          textManager.textList = [`Place your finger`, "on the sensor"];
          break;
        case "fadein":
          textManager.textList = ["Setup DNA Unlock"];
          break;
        case "again?":
          textManager.textList = ["easy."];
          break;}

      time = 0;
    }
    requestAnimationFrame(renderFunction);
  }
  requestAnimationFrame(renderFunction);
}

function drawPhone(
resp,
ctx,
angle,
colors,
{ phoneCenter, phoneBackPoints, phoneDisplayPoints, phoneFrontPoints })
{
  const mat = buildMatrix([
  LMat4.trans(resp.rasterSize.w / 2, resp.rasterSize.h / 2, 0),
  getPhoneRotationMat(resp, angle),
  LMat4.scale(3 * resp.anchor),
  LMat4.scale(0.2),
  LMat4.trans(-phoneCenter.x, -phoneCenter.y, 0)]);

  // back of phone
  const pp3a = phoneBackPoints.map(x => mat.multLV3(x));
  ctx.fillStyle = colors.bg;
  ctx.strokeStyle = colors.outline;
  drawPath(ctx, pp3a, true);

  // front of phone
  const pp1a = phoneFrontPoints.map(x => mat.multLV3(x));
  ctx.fillStyle = colors.bg;
  ctx.strokeStyle = colors.outline;
  fillPath(ctx, pp1a, true);
  drawPath(ctx, pp1a, true);

  // draw inner part of phone
  ctx.fillStyle = colors.bg;
  ctx.strokeStyle = colors.outline;
  const pp2a = phoneDisplayPoints.map(x => mat.multLV3(x));
  drawPath(ctx, pp2a, true);
}

function drawFinger(resp, ctx, time2, colors, fingerAnimator) {
  const time = Math.max(0, Math.min(time2, 1));
  const toFinger = t => {
    let finger = "b";
    if (t <= 0.45) finger = "c";else
    if (t >= 0.95) finger = "a";

    if (t <= 0.5) {
      const pa = fingerAnimator.getTranslations("c");
      const pb = fingerAnimator.getTranslations("b");
      const p = pb.
      sub(pa).
      scale(t / 0.5).
      add(pa);
      return [p, finger];
    } else {
      const pa = fingerAnimator.getTranslations("b");
      const pb = fingerAnimator.getTranslations("a");
      const p = pb.
      sub(pa).
      scale((t - 0.5) / 0.5).
      add(pa);
      return [p, finger];
    }
  };
  const [p, fingerId] = toFinger(time);
  const fingerMat = buildMatrix([
  LMat4.trans(p.x, p.y, 0),
  LMat4.trans(resp.rasterSize.w / 2, resp.rasterSize.h / 2, 0),
  LMat4.scale(0.6 * resp.anchor),
  LMat4.trans(-230, -135, 0)]);

  ctx.strokeStyle = colors.outline;
  fingerAnimator.getFinger(fingerId).forEach(a => {
    const b = a.map(x => fingerMat.multLV3(x));
    drawPath(ctx, b, false);
  });
}

function drawFingerprint(resp, ctx, colors, angle = 0, fingerprintPaths) {
  const fingerprintMatrix = buildMatrix([
  LMat4.trans(resp.rasterSize.w / 2, resp.rasterSize.h / 2, 0),
  getPhoneRotationMat(resp, angle),
  LMat4.scale(0.23 * resp.anchor),
  LMat4.trans(-200, -200, 0)]);

  ctx.strokeStyle = colors.fingerprint;
  fingerprintPaths.forEach(a => {
    const b = a.map(x => fingerprintMatrix.multLV3(x));
    drawPath(ctx, b, false);
  });
}

function drawNeedles(needles, resp, ctx, angle, delta) {
  const needlesMat = buildMatrix([
  LMat4.trans(resp.rasterSize.w / 2, resp.rasterSize.h / 2, 0),
  getPhoneRotationMat(resp, angle),
  LMat4.scale(resp.anchor * 0.6)]);


  needles.forEach(needle => {
    needle.draw(ctx, needlesMat);
    needle.tick(delta * 0.05);
  });
}

function drawLines(lines, resp, ctx, angle, delta) {
  ctx.strokeStyle = "red";
  const linesMat = buildMatrix([
  LMat4.trans(resp.rasterSize.w / 2, resp.rasterSize.h / 2, 0),
  getPhoneRotationMat(resp, angle),
  LMat4.scale(resp.anchor * 0.65)]);

  lines.forEach(l => l.draw(ctx, linesMat));
  const newLines = [];
  lines.forEach(l => {
    if (l.isComplete() && !l.spawned) {
      newLines.push(...l.createChildren());
    } else {
      l.tick(delta * 0.003);
    }
  });
  lines.push(...newLines);
}

function drawLock(lockPaths, resp, ctx, time, angle = 0, color) {
  const xOffset = resp.anchor * 18;
  const scale = 0.14;
  const yox = 380;
  const lockMat = buildMatrix([
  LMat4.trans(resp.rasterSize.w / 2 + xOffset, yox * resp.anchor, 0),
  getPhoneRotationMat(resp, angle),
  LMat4.scale(scale * resp.anchor),
  LMat4.trans(-200, -200, 0)]);

  ctx.fillStyle = color;
  fillPath(
  ctx,
  lockPaths[0].map(x => lockMat.multLV3(x)),
  true);

  const lockMat2 = buildMatrix([
  LMat4.trans(resp.rasterSize.w / 2 + xOffset, yox * resp.anchor, 0),
  getPhoneRotationMat(resp, angle),
  LMat4.scale(scale * resp.anchor),

  // rotate
  LMat4.trans(65, 0, 0),
  LMat4.rotateY(time * 180),
  LMat4.trans(-65, 0, 0),

  //end rotate
  LMat4.trans(-200, -200, 0)]);

  fillPath(
  ctx,
  lockPaths[1].map(x => lockMat2.multLV3(x)),
  true);

}

function drawGlitchedScreen(glitchManager, resp, canvasFrom, ctxTo, colors) {
  ctxTo.fillStyle = colors.bg;
  ctxTo.fillRect(0, 0, resp.rasterSize.w, resp.rasterSize.h);
  ctxTo.drawImage(canvasFrom, 0, 0, resp.rasterSize.w, resp.rasterSize.h);
  const cvWidth = resp.rasterSize.w;
  function createGlitch(ypos, sz = 2, N = 5) {
    if (ypos + N * 2 >= resp.rasterSize.h) return;
    for (let i = 0; i < N; i++) {
      const xCopyAmount = cvWidth - i * sz;
      ctxTo.drawImage(
      canvasFrom,
      0, // x from
      ypos + i, // y from
      xCopyAmount, // copy width
      sz, // copy height
      i * sz, // x from
      ypos + i, // y from
      xCopyAmount, // copy over width
      sz // copy over height
      );
    }
    const yp2 = N + ypos;
    for (let j = 0; j < N; j++) {
      const i = N - j;
      const xTo = N * sz - i * sz;
      const xCopyAmount = cvWidth - xTo;
      ctxTo.drawImage(
      canvasFrom,
      0, // x from
      yp2 + i, // y from
      xCopyAmount, // copy width
      sz,
      xTo, // x to
      yp2 + i, // y to
      xCopyAmount,
      sz);

    }
  }
  glitchManager.glitches.forEach(g => createGlitch(g.ypos, g.sz, g.N));
}

function getPathPoints(pathString) {
  const cp = compilePath(pathString);
  return computePoints(cp);
}

function getPhoneCenter(phoneFrontPoints) {
  let phoneCenter = new LV2(0, 0);
  const toLV2 = x => new LV2(x.x, x.y);
  let min = toLV2(phoneFrontPoints[0]),
  max = toLV2(phoneFrontPoints[0]);
  phoneFrontPoints.forEach(pp => {
    const p = toLV2(pp);
    min = minLV2(min, p);
    max = maxLV2(max, p);
  });
  phoneCenter = max.sub(min).scale(0.5);
  phoneCenter.x = Math.floor(phoneCenter.x);
  phoneCenter.y = Math.floor(phoneCenter.y);
  return phoneCenter;
}

function circle(ctx, p, rad, color = "white") {
  ctx.strokeStyle = color;
  ctx.beginPath();
  ctx.arc(p.x, p.y, rad, 0, 2 * Math.PI);
  ctx.closePath();
  ctx.stroke();
}

function getPhoneRotationMat(resp, angle) {
  return buildMatrix([
  LMat4.trans(-angle * resp.anchor * 2, angle * resp.anchor * 2, 0),
  LMat4.rotateY(angle),
  LMat4.rotateZ(-angle / 8)]);

}

function computeRes() {
  const width = window.innerWidth;
  const height = window.innerHeight;
  const currentResp = width / height;
  const res = 2 / 3;

  let styleSize = { w: 0, h: 0 };

  if (currentResp >= res) {
    // landscape
    styleSize.h = Math.min(800, height - 2);
    styleSize.w = Math.floor(res * styleSize.h);
  } else {
    // portait
    styleSize.w = Math.min(450, width - 2);
    styleSize.h = Math.floor(styleSize.w / res);
  }

  const rasterSize = {
    w: styleSize.w * window.devicePixelRatio,
    h: styleSize.h * window.devicePixelRatio };


  const anchor = rasterSize.w * 0.0012;
  return {
    styleSize,
    rasterSize,
    anchor };

}

class Line {
  constructor(p, v, len, decendent = 0) {
    this.p = p;
    this.v = v;
    this.len = len;
    this.decendent = decendent;
    this.spawned = false;
    this.t = 0;
  }

  draw(ctx, t) {
    const end = this.p.add(this.v.scale(this.len * this.t));
    const a = t.multLV3(this.p);
    const b = t.multLV3(end);
    ctx.beginPath();
    ctx.moveTo(a.x, a.y);
    ctx.lineTo(b.x, b.y);
    ctx.stroke();
  }

  tick(am = 0.2) {
    this.t += am;
    this.t = Math.min(1, this.t);
  }

  isComplete() {
    return this.t >= 1;
  }

  static choice(N) {
    return Math.floor(Math.random() * N);
  }

  createChildren() {
    if (this.decendent > 80) return [];
    // possibilities 0deg, -30deg, 30deg
    const numChildren =
    this.decendent < 3 ?
    Line.choice(3) + 1 :
    Line.choice(3) + Line.choice(3) - 1;
    const childrenMade = new Set();
    const children = [];
    const end = this.p.add(this.v.scale(this.len));
    for (let i = 0; i < numChildren; i++) {
      const deg = Line.choice(3);
      if (!childrenMade.has(deg)) {
        const p = end.copy();
        let v = this.v.copy();
        const angle = deg === 0 ? 0 : deg === 1 ? -30 : 30;
        const mat = LMat4.rotateZ(angle);
        v = mat.multLV3(v).unit();
        const newLen = Math.max(
        this.len * (1 + (Math.random() * 0.25 - 0.1)),
        10);

        if (Line.inPhoneDisplay(p)) {
          children.push(new Line(p, v, newLen, this.decendent + 1));
          childrenMade.add(deg);
        }
      }
    }
    this.spawned = true;
    return children;
  }

  static inPhoneDisplay(p) {
    const mn = new LV2(-202, -458);
    const mx = new LV2(245, 548);
    return p.x >= mn.x && p.x <= mx.x && p.y >= mn.y && p.y <= mx.y;
  }

  static createInitialList() {
    const lines = [];
    const lineCenters = new LV3(0, 0, 0);
    const N = 25;
    const radius = 60;
    for (let i = 0; i < N; i++) {
      const angle = 360 * i / N + (Math.random() - 0.5) * 5;
      const vec = new LV3(Math.cos(angle / 57.3), Math.sin(angle / 57.3), 0);
      const p = lineCenters.add(vec.scale(radius));
      const LL = new Line(p, vec.unit(), 20 + Math.random() * 7);
      lines.push(LL);
    }
    return lines;
  }}


class FingerAnimator {
  constructor(fa, fb, fc, resp) {
    this.pa = svgStringToPathList(fa).map(x => getPathPoints(x).map(toLV3));
    this.pb = svgStringToPathList(fb).map(x => getPathPoints(x).map(toLV3));
    this.pc = svgStringToPathList(fc).map(x => getPathPoints(x).map(toLV3));
    this.resp = resp;
  }

  getFinger(type) {
    switch (type) {
      case "c":
        return this.pc;
      case "b":
        return this.pb;
      case "a":
      default:
        return this.pa;}

  }

  getTranslations(type) {
    const resp = this.resp;
    switch (type) {
      case "b":
        return this.getTranslations("a").sub(
        new LV3(130 * resp.anchor, 90 * resp.anchor, 0));

      case "c":
        return this.getTranslations("b").sub(
        new LV3(80 * resp.anchor, 80 * resp.anchor, 0));

      case "a":
      default:
        return new LV2(0, 0);}

  }}


class Needle {
  constructor(p, offsetPerc) {
    this.p = p;
    this.baseLen = 6;
    this.len = 120;
    this.bloodPercent = Math.random() * 0.3 + 0.15;
    // compute angle
    this.radians = Math.atan(this.baseLen * 0.5 / this.len);
    this.percentOut = 1;
    this.spd = Math.floor(50 + Math.random() * 40);
    this.t = Math.floor((offsetPerc || 0) * this.spd);
  }

  tick(am = 1) {
    this.t += am;
    const spd = this.spd;
    const hlv = spd * 0.5;
    const qt = spd * 0.25;
    this.t %= spd;
    if (this.t <= qt) {
      this.percentOut = this.t / qt;
    } else if (this.t <= hlv) {
      this.percentOut = 1 - (this.t - qt) / qt;
    } else {
      this.percentOut = 0;
    }
    // this.percentOut = (Math.sin(this.t * 0.2)+1) / 2;
  }

  computePoints() {
    const length = this.len * this.percentOut;
    const tip = this.p.add(new LV3(0, 0, length));
    const b1 = this.p.add(new LV3(0, -length * Math.tan(this.radians), 0));
    const b2 = this.p.add(new LV3(0, length * Math.tan(this.radians), 0));
    let blood1, blood2;
    const bloodLength = this.len * this.bloodPercent;
    if (length >= this.bloodPercent * this.len) {
      blood1 = tip.add(
      new LV3(
      0,
      -Math.sin(this.radians) * bloodLength,
      -Math.cos(this.radians) * bloodLength));


      blood2 = tip.add(
      new LV3(
      0,
      Math.sin(this.radians) * bloodLength,
      -Math.cos(this.radians) * bloodLength));


    }

    return [this.p.copy(), b1, b2, tip, blood1, blood2].map(
    x => x && x.add(new LV3(0, 0, 0)));

  }

  draw(ctx, mat) {
    if (this.percentOut <= 0) return;
    const ptz = this.computePoints().map((x) =>
    x ? mat.multLV3(x) : undefined);

    const v = ptz[3].sub(ptz[0]).unit();
    if (v.dot(new LV3(0, 0, -1)) > 0.6) {
      return;
    }

    ctx.fillStyle = "#8C9995";
    fillPath(ctx, ptz.slice(1, 4), true);
    if (ptz[4] && ptz[5]) {
      ctx.fillStyle = "red";
      fillPath(ctx, ptz.slice(3, 6), "red");
    }
  }

  static createInitialList() {
    const needles = [];
    const needlesGen = [
    { rad: 20, N: 5 },
    { rad: 30, N: 10 },
    { rad: 40, N: 15 }];

    needlesGen.forEach(({ rad, N }) => {
      for (let i = 0; i < N; i++) {
        const angle = i / N * 360;
        const vec = new LV3(
        Math.cos(angle / 57.3),
        Math.sin(angle / 57.3),
        0).
        scale(rad);
        const n = new Needle(new LV3(vec.x, vec.y, 0), Math.random());
        needles.push(n);
      }
    });
    return needles;
  }}


class GlitchManager {
  constructor(resp) {
    this.resp = resp;
    this.N = 10;
    this.glitches = [];
    this.t = 0;
    this.refreshRate = 200;
    this.refresh();
  }

  tick(dt) {
    this.t += dt;
    if (this.t >= this.refreshRate) {
      this.refresh();
      this.t -= this.refreshRate;
    }
  }

  refresh() {
    const resp = this.resp;
    const N = Math.random() * this.N;
    const newG = [];
    for (let i = 0; i < N; i++) {
      newG.push({
        ypos: Math.floor(Math.random() * (resp.rasterSize.h - 1)),
        N: Math.floor(
        Math.floor(Math.random() * 5 * resp.anchor) * 2 + 8 * resp.anchor),

        sz: Math.max(
        Math.floor(Math.random() * 3 * resp.anchor + 2 * resp.anchor),
        1) });


    }
    this.glitches = newG;
  }}


class TextManager {
  constructor(resp, textList, multiline = true) {
    this.resp = resp;
    this.textList = textList;
    this.textIndex = 0;
    this.multiline = multiline;
  }

  draw(ctx, T, colors) {
    if (this.textList.length === 0) return;
    const resp = this.resp;
    const text = this.getText(T);
    if (!text) return;
    this.setupDraw(ctx, colors);
    if (!this.multiline) {
      ctx.fillText(
      text,
      resp.rasterSize.w / 2 - resp.anchor * text.length * 7.8,
      resp.rasterSize.h - resp.anchor * 120);

    } else {
      for (let i = 0; i < text.length; i++) {
        const t = text[i];
        ctx.fillText(
        t,
        resp.rasterSize.w / 2 - resp.anchor * t.length * 7.8,
        resp.rasterSize.h - resp.anchor * 120 + resp.anchor * i * 42);

      }
    }
  }

  getText(T) {
    if (this.multiline) {
      return this.textList;
    }
    return this.textList[Math.floor(T * this.textList.length)];
  }

  setupDraw(ctx, colors) {
    const resp = this.resp;
    ctx.fillStyle = colors.outline;
    ctx.font = `${
    36 * resp.anchor
    }px SF Pro Display,SF Pro Icons,Helvetica Neue,Helvetica,Arial,sans-serif`;
  }}


const BZC = {
  /**
               * @param {number} n
               * @param {number} i
               */
  nChooseI(n, i) {
    return this.factorial(n) / (this.factorial(i).........完整代码请登录后点击上方下载按钮下载查看

网友评论0