svg实现线条绘制三角canvas动画效果代码

代码语言:html

所属分类:动画

代码描述:svg实现线条绘制三角canvas动画效果代码

代码标签: 绘制 三角 canvas 动画 效果

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

<!DOCTYPE html>
<html lang="en" >

<head>

  <meta charset="UTF-8">



  
  
  
  
<style>
document, body {
  margin: 0;
  min-height: 100vh;
}

body {
  align-items: center;
  display: flex;
  justify-content: center;
}

#container {
  align-items: center;
  display: flex;
  flex-direction: column;
}

#container > :first-child {
  cursor: pointer;
}

button {
  max-width: 200px;
  margin-top: 10px;
}

canvas, svg {
  width: 100vw;
  height: 100vh;
}
</style>



</head>

<body >
  <div id="container"></div>
<script type="text/javascript" src="//repo.bfw.wiki/bfwrepo/js/svg.min.js"></script>
      <script type="module">

import { Vec2 } from 'https://cdn.skypack.dev/wtc-math';
console.clear();

const config = {
  drawingType: 1,
  dimensions: (new Vec2(window.innerWidth, window.innerHeight)).scale(2),
  breakRun: 2000,
  maxDepth: 12,
  maxPerRun: 100,
  insertType: 1,
  randomType: 0
};
const vars = {
  drawing: null,
  i:0,
  running: true,
  triangles: []
}
const setup = () => {
  vars.running = false;
  setTimeout(() => {
    vars.running = true;
    vars.triangles = [];
    config.insertType = Math.floor(Math.random() * 3);
    config.randomType = Math.floor(Math.random() * 3);
    vars.i=0;
    
    document.querySelector('#container').innerHTML = '';
    vars.drawing = new Drawing(config.drawingType).addTo('#container').size(config.dimensions);
    document.body.querySelector('#container>:first-child').addEventListener('click', () => {
      setup();
    });
    
    draw();
  }, 100);
};

window.addEventListener('resize', () => {
  config.dimensions = ( new Vec2(window.innerWidth, window.innerHeight)).scale(2)
  setup();
});

let depth = 0;
const ts = [
  ['a', 'b'],
  ['b', 'c'],
  ['c', 'a']
];
const drawStep = () => {
  if(!vars.running) return;
  if(vars.i > 2000) return;
  let newTriangles = [];
  vars.triangles.forEach((triangle,i) => {
    if(i>config.breakRun) {
      // newTriangles.splice(0,0,triangle);
      return;
    }
    vars.drawing.polygon(triangle.points);
    let c = triangle.randomCentroid;
    if(config.randomType == 0) {
      c = triangle.centroid;
    } 
    
    // vars.drawing.circle(c, 5);
    if(triangle.depth < config.maxDepth) {
      ts.forEach(t => {
        const tr = new Triangle(triangle[t[0]], triangle[t[1]], c);
        tr.depth=triangle.depth+1;
        newTriangles.push(tr); 
      });
    }
  });
  if(config.insertType === 0) {
    if(vars.triangles.length>config.breakRun) newTriangles = vars.triangles.splice(config.breakRun).concat(newTriangles);
  } else if(config.insertType === 1) {
    if(vars.triangles.length>config.breakRun) newTriangles = vars.triangles.splice(config.breakRun).reverse().concat(newTriangles);
  } else {
    newTriangles = newTriangles.concat(vars.triangles.splice(config.breakRun));
  }
  
  vars.triangles = newTriangles;
  vars.i++;
  requestAnimationFrame(drawStep);
}
let interval;
const draw = () => {
  vars.drawing.linewidth = 1;
  vars.drawing.fill = "#333";
  vars.drawing.rect(new Vec2(0,0), config.dimensions);
  vars.drawing.linewidth = 1;
  vars.drawing.fill = null;
  vars.drawing.stroke = 'rgba(255,255,255,.02)'
  
  const r = Math.min(config.dimensions.x, config.dimensions.y) * .6;
  const offset = r * 0.2333;
  const points = [];
  for(let i = 0.; i < Math.PI * 2; i += Math.PI * 2. / 3.) {
    points.push( new Vec2(Math.cos(i+Math.PI*.5) * r + config.dimensions.x / 2, Math.sin(i+Math.PI*.5) * r + config.dimensions.y / 2 - offset) );
  }
  
  const t = new Triangle(...points);
 t.depth=0;
  vars.triangles.push(t);
  
  drawStep();
}

class Triangle {
  
  #points
  
  constructor(a, b, c) {
    this.points = [a, b, c];
  }
  
  set points(p) {
    if(p.reduce((a, c) => c instanceof Vec2 ? a+1 : 0, 0) === 3) this.#points = p;
  }
  get points() {
    return this.#points || [];
  }
  
  get a() {
    return this.#points[0];
  }
  get b() {
    return this.#points[1];
  }
  get c() {
    return this.#points[2];
  }
  
  get circumcenter() {
    const d = this.b.subtractNew(this.a);
    const e = this.c.subtractNew(this.a);

    const bl = d.x * d.x + d.y * d.y;
    const cl = e.x * e.x + e.y * e.y;
    const ds = 0.5 / (d.x * e.y - d.y * e.x);
    
    return new Vec2(
      this.a.x + (e.y * bl - d.y * cl) * ds,
      this.a.y + (d.x * cl - e.x * bl) * ds
    );
  }
  get randomCentroid() {
    let a = floatRandomBetween(.2,1.8);
    let b = floatRandomBetween(.2,1.8);
    let c = floatRandomBetween(.2,1.8);
    let abc = a+b+c;
    const f = (3-abc)/3;
    a += f;
    b += f;
    c += f;
    return this.a.scaleNew(a).add(this.b.scaleNew(b)).add(this.c.scaleNew(c)).divideScalar(3);
  }
  get centroid() {
    const a = floatRandomBetween(.5,1.5);
    const b = floatRandomBetween(.5,1.5);
    const c = floatRandomBetween(.5,1.5);
    const abc = a+b+c;
    return this.a.addNew(this.b).add(this.c).divideScalar(3);
  }
  
  get isComplete() {
    return this.points.reduce((a, c) => c instanceof Vec2 ? a+1 : 0, 0) === 3;
  }
  
  get segments() {
    return [
      {
        a: this.a,
        b: this.b,
        c: this.c,
        segment: this.a.subtractNew(this.b)
      },
      {
        a: this.b,
        b: this.c,
        c: this.a,
        segment: this.b.subtractNew(this.c)
      },
      {
        a: this.c,
        b: this.a,
        c: this.b,
        segment: this.c.subtractNew(this.a)
      }
    ];
  }
  
  get angles() {
    let ab = this.b.subtractNew(this.a).normalise();
    let ac = this.c.subtractNew(this.a).normalise();
    
    let ba = this.a.subtractNew(this.b).normalise();
    let bc = this.c.subtractNew(this.b).normalise();
    
    const ta = (Math.acos(ab.dot(ac)));
    const tb = (Math.acos(ba.dot(bc)));
    const tc = (Math.acos(ac.dot(bc)));
    
    return [ta,tb,tc];
  }
  
  get isEqualateral() {
    return this.numEqualSides === 3;
  }
  get isIsosceles() {
    return this.numEqualSides === 2;
  }
  get isScalene() {
    return this.numEqualSides === 1;
  }
  get isRightAngle() {
    return this.angles.reduce((n, s) => n || precisionRound(Math.abs(s * 180 / Math.PI), 3) === 90, false);
  }
  get isObtuse() {
    return this.angles.reduce((n, s) => n || precisionRound(Math.abs(s * 180 / Math.PI), 3) > 90, false);
  }
  get isAcute() {
    return !this.isObtuse;
  }
  
  get numEqualSides() {
    const ls = this.segments.map(seg => precisionRound(seg.segment.length, 3));
    const n = ls.map(l => ls.reduce((n, l1) => n + (l1 === l), 0));
    return n.reduce((a, b) => b > a ? b : a, 0);
  }
  
  get hypot() {
    const segs = this.segments;
    let longest = 0;
    let hypotenuse = {};
    segs.forEach(seg => {
      if(seg.segment.length > longest) {
        longest = seg.segment.length;
        hypotenuse = seg;
      }
    });
    return hypotenuse;
  }
}

setTimeout(() => {
  setup();
}, 500);

class Drawing {
  static #defaults = {
    stroke: '#333',
    pxratio: 1
  }
  static DT_CANVAS = 1;
  static DT_SVG = 2;
  #drawing;
  #ctx;
  #mode;
  #instructions = [];
  
  constructor(mode = Drawing.DT_CANVAS, settings) {
    settings = Object.assign({}, Drawing.#defaults, settings);
    
    this.mode = mode;
    if(this.mode & Drawing.DT_CANVAS) {
      this.#drawing = document.createElement('canvas');
    } else if(this.mode & Drawing.DT_SVG) {
      this.#drawing = SVG();
    }
    
    this.stroke = settings.stroke;
    this.pxratio = settings.pxratio;
  }
  clear() {
    if(this.mode & Drawing.DT_CANVAS) {
      this.c.clearRect(0,0,...this.dimensions.array);
    } else if(this.mode & Drawing.DT_SVG) {
      this.drawing.clear();
    }
  }
  rect(position, dimensions) {
    if(this.saving) {
      this.#instructions.push({
        f: 'rect',
        args: [position, dimensions]
      });
    }
    position = position.scaleNew(this.pxratio);
    dimensions = dimensions.scaleNew.........完整代码请登录后点击上方下载按钮下载查看

网友评论0