canvas文字方块化闪动粒子动画效果代码

代码语言:html

所属分类:粒子

代码描述:canvas文字方块化闪动粒子动画效果代码

代码标签: 闪动 粒子 动画 效果

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

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

<head>

  <meta charset="UTF-8">
  

  
  
  
<style>
#input {
  position: absolute;
  bottom: 10px;
  left: 50%;
  width: 8em;
  max-width: 80%;
  background: none;
  border: none;
  outline: none;
  border-bottom: 2px solid #fff;
  color: #fff;
  font-size: 3em;
  text-align: center;
  z-index: 999;
  opacity: 0.25;
  transform: translateX(-50%);
  transition: opacity 0.3s;
}
#input:hover, #input:focus {
  opacity: 1;
}

body {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
  -webkit-user-select: none;
     -moz-user-select: none;
      -ms-user-select: none;
          user-select: none;
}
</style>

 

</head>

<body  >
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">

<input type="text" id="input" placeholder="type whatever" value="#countdown" title="type and press enter" />
<script type="text/javascript" src="//repo.bfw.wiki/bfwrepo/js/underscore.js"></script>
      <script >
const STEP_LENGTH = 1;
const CELL_SIZE = 10;
const BORDER_WIDTH = 2;
const MAX_FONT_SIZE = 500;
const MAX_ELECTRONS = 100;
const CELL_DISTANCE = CELL_SIZE + BORDER_WIDTH;

// shorter for brighter paint
// be careful of performance issue
const CELL_REPAINT_INTERVAL = [
300, // from
500 // to
];

const BG_COLOR = '#1d2227';
const BORDER_COLOR = '#13191f';
const CELL_HIGHLIGHT = '#328bf6';
const ELECTRON_COLOR = '#00b07c';
const FONT_COLOR = '#ff5353';

const FONT_FAMILY = 'Helvetica, Arial, "Hiragino Sans GB", "Microsoft YaHei", "WenQuan Yi Micro Hei", sans-serif';

const DPR = window.devicePixelRatio || 1;

const ACTIVE_ELECTRONS = [];
const PINNED_CELLS = [];

const MOVE_TRAILS = [
[0, 1], // down
[0, -1], // up
[1, 0], // right
[-1, 0] // left
].map(([x, y]) => [x * CELL_DISTANCE, y * CELL_DISTANCE]);

const END_POINTS_OFFSET = [
[0, 0], // left top
[0, 1], // left bottom
[1, 0], // right top
[1, 1] // right bottom
].map(([x, y]) => [
x * CELL_DISTANCE - BORDER_WIDTH / 2,
y * CELL_DISTANCE - BORDER_WIDTH / 2]);


class FullscreenCanvas {
  constructor(disableScale = false) {
    const canvas = document.createElement('canvas');
    const context = canvas.getContext('2d');

    this.canvas = canvas;
    this.context = context;
    this.disableScale = disableScale;

    this.resizeHandlers = [];
    this.handleResize = _.debounce(this.handleResize.bind(this), 100);

    this.adjust();

    window.addEventListener('resize', this.handleResize);
  }

  adjust() {
    const {
      canvas,
      context,
      disableScale } =
    this;

    const {
      innerWidth,
      innerHeight } =
    window;

    this.width = innerWidth;
    this.height = innerHeight;

    const scale = disableScale ? 1 : DPR;

    this.realWidth = canvas.width = innerWidth * scale;
    this.realHeight = canvas.height = innerHeight * scale;
    canvas.style.width = `${innerWidth}px`;
    canvas.style.height = `${innerHeight}px`;

    context.scale(scale, scale);
  }

  clear() {
    const { context } = this;

    context.clearRect(0, 0, this.width, this.height);
  }

  makeCallback(fn) {
    fn(this.context, this);
  }

  blendBackground(background, opacity = 0.05) {
    return this.paint((ctx, { realWidth, realHeight, width, height }) => {
      ctx.globalCompositeOperation = 'source-over';
      ctx.globalAlpha = opacity;

      ctx.drawImage(background, 0, 0, realWidth, realHeight, 0, 0, width, height);
    });
  }

  paint(fn) {
    if (!_.isFunction(fn)) return;

    const { context } = this;

    context.save();

    this.makeCallback(fn);

    context.restore();

    return this;
  }

  repaint(fn) {
    if (!_.isFunction(fn)) return;

    this.clear();

    return this.paint(fn);
  }

  onResize(fn) {
    if (!_.isFunction(fn)) return;

    this.resizeHandlers.push(fn);
  }

  handleResize() {
    const { resizeHandlers } = this;

    if (!resizeHandlers.length) return;

    this.adjust();

    resizeHandlers.forEach(this.makeCallback.bind(this));
  }

  renderIntoView(target = document.body) {
    const { canvas } = this;

    this.container = target;

    canvas.style.position = 'absolute';
    canvas.style.left = '0px';
    canvas.style.top = '0px';

    target.appendChild(canvas);
  }

  remove() {
    if (!this.container) return;

    try {
      window.removeEventListener('resize', this.handleResize);
      this.container.removeChild(this.canvas);
    } catch (e) {}
  }}


class Electron {
  constructor(
  x = 0,
  y = 0,
  {
    lifeTime = 3 * 1e3,
    speed = STEP_LENGTH,
    color = ELECTRON_COLOR } =
  {})
  {
    this.lifeTime = lifeTime;
    this.expireAt = Date.now() + lifeTime;

    this.speed = speed;
    this.color = color;

    this.radius = BORDER_WIDTH / 2;
    this.current = [x, y];
    this.visited = {};
    this.setDest(this.randomPath());
  }

  randomPath() {
    const {
      current: [x, y] } =
    this;

    const { length } = MOVE_TRAILS;

    const [deltaX, deltaY] = MOVE_TRAILS[_.random(length - 1)];

    return [
    x + deltaX,
    y + deltaY];

  }

  composeCoord(coord) {
    return coord.join(',');
  }

  hasVisited(dest) {
    const key = this.composeCoord(dest);

    return this.visited[key];
  }

  setDest(dest) {
    this.destination = dest;
    this.visited[this.composeCoord(dest)] = true;
  }

  next() {
    let {
      speed,
      current,
      destination } =
    this;

    if (Math.abs(current[0] - destination[0]) <= speed / 2 &&
    Math.abs(current[1] - destination[1]) <= speed / 2)
    {
      destination = this.randomPath();

      let tryCnt = 1;
      const maxAttempt = 4;

      while (this.hasVisited(destination) && tryCnt <= maxAttempt) {
        tryCnt++;
        destination = this.randomPath();
      }

      this.setDest(destination);
    }

    const deltaX = destination[0] - current[0];
    const deltaY = destination[1] - current[1];

    if (deltaX) {
      current[0] += deltaX / Math.abs(deltaX) * speed;
    }

    if (deltaY) {
      current[1] += deltaY / Math.abs(deltaY) * speed;
    }

    return [...this.current];
  }

  paintNextTo(layer = new FullscreenCanvas()) {
    const {
      radius,
      color,
      expireAt,
      lifeTime } =
    this;

    const [x, y] = this.next();

    layer.paint(ctx => {
      ctx.globalAlpha = Math.max(0, expireAt - Date.now()) / lifeTime;
      ctx.fillStyle = color;
      ctx.shadowColor = color;
      ctx.shadowBlur = radius * 5;
      ctx.globalCompositeOperation = 'lighter';

      ctx.beginPath();
      ctx.arc(x, y, radius, 0, Math.PI * 2);
      ctx.closePath();

      ctx.fill();
    });
  }}


class Cell {
  constructor(
  row = 0,
  col = 0,
  {
    electronCount = _.random(1, 4),
    background = ELECTRON_COLOR,
    forceElectrons = false,
    electronOptions = {} } =
  {})
  {
    this.background = background;
    this.electronOptions = electronOptions;
    this.forceElectrons = forceElectrons;
    this.electronCount = Math.min(electronCount, 4);

    this.startY = row * CELL_DISTANCE;
    this.startX = col * CELL_DISTANCE;
  }

  delay(ms = 0) {
    this.pin(ms * 1.5);
    this.nextUpdate = Date.now() + ms;
  }

  pin(lifeTime = -1 >>> 1) {
    this.expireAt = Date.now() + lifeTime;

    PINNED_CELLS.push(this);
  }

  scheduleUpdate(
  t1 = CELL_REPAINT_INTERVAL[0],
  t2 = CELL_REPAINT_INTERVAL[1])
  {
    this.nextUpdate = Date.now() + _.random(t1, t2);
  }

  paintNextTo(layer = new FullscreenCanvas()) {
    const {
      startX,
      startY,
      background,
      nextUpdate } =
    this;

    if (nextUpdate && Date.now() < nextUpdate) return;

    this.scheduleUpdate();
    this.createElectrons();

    layer.paint(ctx => {
      ctx.globalCompositeOperation = 'lighter';
      ctx.fillStyle = background;
      ctx.fillRect(startX, startY, CELL_SIZE, CELL_SIZE);
    });
  }

  popRandom(arr = []) {
    const ramIdx = _.random(arr.length - 1);

    return arr.splice(ramIdx, 1)[0];
  }

  createElectrons() {
    const {
      startX,
      startY,
      electronCount,
      electronOptions,
      forceElectrons } =
    this;

    if (!electronCount) return;

    const endpoints = [...END_POINTS_OFFSET];

    const max = forceElectrons ? electronCount : Math.min(electronCount, MAX_ELECTRONS - ACTIVE_ELECTRONS.length);

    for (let i = 0; i < max; i++) {
      const [offsetX, offsetY] = this.popRandom(endpoints);

      ACTIVE_ELECTRONS.push(new Electron(
      startX + offsetX,
      startY + offsetY,
      electronOptions));

    }
  }}


const bgLayer = new FullscreenCanvas();
const mainLayer = new FullscreenCanvas();
const shapeLayer = new FullscreenCanvas(true);

function stripOld(limit = 1000) {
  const now = Date.now();

  for (let i = 0, max = ACTIVE_ELECTRONS.length; i < max; i++) {
    const e = ACTIVE_ELECTRONS[i];

    if (e.expireAt - now < limit) {
      ACTIVE_ELECTRONS.splice(i, 1);

      i--;
      max--;
    }
  }
}

function createRandomCell(options = {}) {
  if (ACTIVE_ELECTRONS.length >= MAX_ELECTRONS) return;

  const { width, height } = mainLayer;

  const cell = new Cell(
  _.random(height / CELL_DISTANCE),
  _.random(width / CELL_DISTANCE),
  options);


  cell.paintNextTo(mainLayer);
}

function drawGrid() {
  bgLayer.paint((ctx, { width, height }) => {
    ctx.fillStyle = BG_COLOR;
    ctx.fillRect(0, 0, width, height);

    ctx.fillStyle = BORDER_COLOR;

    // horizontal lines
   .........完整代码请登录后点击上方下载按钮下载查看

网友评论0