html5 canvas圓形的泡沫動畫特效



79 314 105



特效描述:html5 canvas圓形的泡沫 動畫特效,html5 canvas繪制圓形泡沫動畫,鼠標點擊會變顏色效果。

代碼結構

1. HTML代碼

<div class="container">
  <canvas class="canvas" id="canvas"></canvas>
</div>
<script>
'use strict';
(function() {
  var canvas = document.getElementById('canvas'),
    ctx = canvas.getContext('2d'),
    mouseX = 0, mouseY = -100,
    width = window.innerWidth, height = window.innerHeight,
    isMouseMove = false;
  var TAU = 2 * Math.PI;
  var generates = 0;
  var attempts = 300;
  var mainCirlce = {
    x: width / 2,
    y: height / 2,
    rds: 220
  };
  var maxCirlcesCount = 700;
  var circles = [];
  var maxCircleSize = mainCirlce.rds / 7;
  var colors = [
    ['#0F4155', '#F5C03E'],
    ['#D63826', '#0F4155'],
    ['#0F4155', '#D63826'],
    ['#152A3B', '#158ca7'],
    ['#e4a50a', '#D63826'],
    ['#D63826', '#e4a50a'],
    ['#3a7b30', '#F5C03E'],
    ['#0F4155', '#7ec873']
  ];
  var mouseCircle = {
    currX: width / 2,
    currY: -100,
    targX: width / 2,
    targY: -100,
    rds: maxCircleSize * .85,
    easingCircle: .15,
    easingRect: .13,
    angle: random(360),
    rectCurrX: width / 2,
    rectCurrY: -100,
  };
  var activeDistanceRadius = maxCircleSize * 2;
  var isRectPosOfsset = false;
  var isRectWidthOfsset = false;
  var isDrawBubbles = true;
  var isDrawRects = true;
  var rndWidthFactor = 1;
  var rndXOffsetFactor = .5;
  var rndYOffsetFactor = .5;
  var sintId;
  function start() {
    setup();
  }
  function setup() {
    canvas.width  = width;
    canvas.height = height;
    ctx.lineWidth = 1;
    sintId = setInterval(function () {
      generateNewWorld();
    }, 500);
    setEventListeners();
    draw();
  }
  function generateNewWorld() {
    if (generates === 3) {  clearInterval(sintId);  }
    isRectPosOfsset = false;
    isRectWidthOfsset = false;
    isDrawBubbles = true;
    isDrawRects = true;
    rndWidthFactor = 1;
    rndXOffsetFactor = .5;
    rndYOffsetFactor = .5;
    mouseCircle.angle = random(360);
    var clrSetIdx = ~~random(colors.length);
    ctx.strokeStyle = colors[clrSetIdx][0];
    ctx.fillStyle   = colors[clrSetIdx][1];
    if (generates === 0) {
      isDrawRects = false;
    }
    if (generates === 1) {
      isRectPosOfsset = true;
      rndXOffsetFactor = random(-.5, 1);
      rndYOffsetFactor = random(-.5, 1);
    }
    if (generates === 2) {
      isRectWidthOfsset = true;
      rndWidthFactor = random(4, 5);
    }
    if (generates > 3) {
      if (Math.random() >= .8) {
        isRectWidthOfsset = true;
        rndWidthFactor = random(3, 5);
      }
      if (Math.random() >= .7) {
        isRectPosOfsset = true;
        rndXOffsetFactor = random(-.5, 1);
        rndYOffsetFactor = random(-.5, 1);
      }
      if (Math.random() >= .85) {
        isDrawBubbles = false;
      }
      if (isDrawBubbles) {
        if (Math.random() >= .85) {
          isDrawRects = false;
        }
      }
    }
    generateCirclesInCircle();
    ++generates;
  }
  function draw() {
    background('#fff');
    drawFoamBubbles();
    drawMouseCircle();
    window.requestAnimationFrame(draw);
  }
  function drawFoamBubbles() {
    updateCirclesPos(circles);
    circles.forEach(function (p) {
      p.updateAnimationValues();
    });
    if (isDrawRects) {
      circles.forEach(function (p) {
        p.drawRect();
      });
    }
    if (isDrawBubbles) {
      circles.forEach(function (p) {
        p.drawCircle();
      });
    }
  }
  function drawMouseCircle() {
    mouseCircle.currX += (mouseX - mouseCircle.currX) * mouseCircle.easingCircle;
    mouseCircle.currY += (mouseY - mouseCircle.currY) * mouseCircle.easingCircle;
    mouseCircle.rectCurrX += (mouseX - mouseCircle.rectCurrX) * mouseCircle.easingRect;
    mouseCircle.rectCurrY += (mouseY - mouseCircle.rectCurrY) * mouseCircle.easingRect;
    ctx.beginPath();
    ctx.arc(mouseCircle.currX, mouseCircle.currY, mouseCircle.rds, 0, Math.PI * 2, false);
    ctx.arc(mouseCircle.currX, mouseCircle.currY, mouseCircle.rds + .3, 0, Math.PI * 2, false);
    ctx.stroke();
    ctx.save();
    ctx.translate(mouseCircle.rectCurrX, mouseCircle.rectCurrY);
    ctx.rotate(mouseCircle.angle);
    ctx.fillRect(-mouseCircle.rds * rndXOffsetFactor, -mouseCircle.rds * rndYOffsetFactor, mouseCircle.rds / rndWidthFactor, mouseCircle.rds);
    ctx.restore();
  }
  function generateCirclesInCircle() {
    circles = [];
    var newCircleCenter, tmpDistance, cDist, idx, itr, i;
    for (idx = 0; idx < maxCirlcesCount; idx++) {
      for (i = 0; i < attempts; ++i) {
        newCircleCenter = genNewPosition(mainCirlce);
        if (isPointInsideCircle(newCircleCenter, mainCirlce) && !isIntersectWithCircles(newCircleCenter, circles)) {
          break;
        }
      }
      // distance to main circle border;
      var newCircleSize = (mainCirlce.rds - dist(newCircleCenter.x, newCircleCenter.y, mainCirlce.x, mainCirlce.y)) * 2;
      newCircleSize = newCircleSize > maxCircleSize ? maxCircleSize : newCircleSize;
      for (itr = 0; itr < circles.length; itr++) {
        cDist = dist(circles[itr].x, circles[itr].y, newCircleCenter.x, newCircleCenter.y);
        tmpDistance = Math.abs(circles[itr].rds - cDist);
        if (tmpDistance < newCircleSize) {
          newCircleSize = tmpDistance + 3;
        }
      }
      if (newCircleSize >= 3) {
        circles.push(new Circle(newCircleCenter.x, newCircleCenter.y, newCircleSize, idx));
      }
    }
    if (isRectWidthOfsset) {
      circles.forEach(function (circleItem) {
        circleItem.rectWidthDivideFactor = rndWidthFactor;
      });
    }
    if (isRectPosOfsset) {
      circles.forEach(function (circleItem) {
        circleItem.rectXOffsetFactor = rndXOffsetFactor;
        circleItem.rectYOffsetFactor = rndYOffsetFactor;
      });
    }
  }
  function updateCirclesPos() {
    var i, circleItem, theta, distance;
    for (i = 0; i < circles.length; i++) {
      circleItem = circles[i];
      theta = Math.atan2(circleItem.y - mouseY, circleItem.x - mouseX);
      distance = activeDistanceRadius / dist(mouseX, mouseY, circleItem.x, circleItem.y);
      circleItem.x += Math.cos(theta) * distance + (circleItem.origX - circleItem.x) * 0.075;
      circleItem.y += Math.sin(theta) * distance + (circleItem.origY - circleItem.y) * 0.075;
    }
  }
  // Circle class
  function Circle (x, y, r, idx) {
    this.x = x;
    this.y = y;
    this.origX = x;
    this.origY = y;
    this.rds = r;
    this.currRds = 0;
    this.idx = idx;
    this.durAnimation = random(1.7, 2.7);
    this.durProgress = 0;
    this.currAngle = random(1, 7);
    this.targetAngle = this.currAngle + random(1, 7) * (Math.random() > .5 ? 1 : -1);
    this.rectWidth = 0;
    this.rectX = 0;
    this.rectY = 0;
    this.rectXOffsetFactor = .5;
    this.rectYOffsetFactor = .5;
    this.rectWidthDivideFactor = 1;
    this.drawCircle = function () {
      ctx.beginPath();
      ctx.arc(this.x, this.y, this.currRds, 0, Math.PI * 2, false);
      ctx.arc(this.x, this.y, this.currRds + .3, 0, Math.PI * 2, false);
      ctx.stroke();
    };
    this.drawRect = function () {
      ctx.save();
      ctx.translate(this.x, this.y);
      ctx.rotate(this.currAngle);
      ctx.fillRect(this.rectX, this.rectY, this.rectWidth, this.currRds);
      ctx.restore();
    };
    this.updateAnimationValues = function () {
      if (this.durProgress < this.durAnimation) {
        if (this.currRds !== this.rds) {
          this.currRds = easeOutElastic(this.durProgress, this.currRds, this.rds, this.durAnimation);
          this.rectWidth = this.currRds / this.rectWidthDivideFactor;
          this.rectX = -this.currRds * this.rectXOffsetFactor;
          this.rectY = -this.currRds * this.rectYOffsetFactor;
        }
        if (this.currAngle !== this.targetAngle) {
          this.currAngle = easeOutElastic(this.durProgress, this.currAngle, this.targetAngle, this.durAnimation);
        }
        this.durProgress += .008;
      }
    };
    this.drawWithAnimate = function () {
      this.updateAnimationValues();
      this.drawCircle();
    };
  }
  function genNewPosition(mainCirlce) {
    return {
      x: random(10, width  - 20),
      y: random(10, height - 10)
    };
    // // different way: perfect for creating point inside circle but having some slight differences in result
    // var angle = random(TAU);
    // var scalar = random(1, mainCirlce.rds);
    // return {
    //   x: Math.cos(angle) * scalar + mainCirlce.x,
    //   y: Math.sin(angle) * scalar + mainCirlce.y
    // };
  }
  function isPointInsideCircle(pos, circle) {
    return  dist (pos.x, pos.y, circle.x, circle.y) < circle.rds;
  }
  function isIntersectWithCircles(pos, circles) {
    if (circles.length > 0) {
      for (var i = 0; i < circles.length; i++) {
        if (isPointInsideCircle(pos, circles[i])) {
          return true;
        }
        // return isPointInsideCircle(pos, circles[i]);
      }
    }
    return false;
  }
  function setEventListeners() {
    document.addEventListener('mousedown', mouseDown);
    document.addEventListener('mouseout',  mouseOut );
    document.addEventListener('mousemove', mouseMove);
    document.addEventListener('keypress',  keyTyped );
    window.addEventListener('resize',  function() {
      width = window.innerWidth; height = window.innerHeight;
    } );
  }
  function keyTyped(evt) {
    var eventKey = evt.key.toLowerCase();
    switch (eventKey){
      case 'g': generateNewWorld(); break;
    }
  }
  function mouseDown(evt) {
    if (evt.button === 0) { generateNewWorld(); }
  }
  function mouseOut (evt) {
    isMouseMove = false;
  }
  function mouseMove(evt) {
    var mousePos = getMousePos(canvas, evt);
    mouseX = mousePos.x;
    mouseY = mousePos.y;
    isMouseMove = true;
  }
  function getMousePos(block, evt) {
    block = block || document.getElementsByTagName('body')[0];
    var rect = block.getBoundingClientRect();
    return {
      x: evt.clientX - rect.left,
      y: evt.clientY - rect.top
    };
  }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Helper Functions //////////////////////////////////////////////////////////////////////////////////////////
  function random (min, max){
    if (!min && min !== 0) {
      return Math.random();
    } else if (!max) {
      return Math.random() * min;
    }
    return Math.random() * (max - min) + min;
  }
  function dist (x1, y1, x2, y2) {
    var a = x1 - x2;
    var b = y1 - y2;
    return Math.sqrt(a * a + b * b);
  }
  function background(clr) {
    ctx.save();
    ctx.fillStyle = clr || "#fff";
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    ctx.restore();
  }
  // t: curr time, b: start val, c: change In value, d: duration
  function easeOutElastic(t, b, c, d) {
    var s = 1.70158;
    var a = c;
    if (t === 0) { return 0; }
    t /= d;
    if (t === 1) { return c; }
    var p = d * 0.3;
    if (a < Math.abs(c)) {
      a = c;
      s = p / 4;
    } else {
      s = p / (2 * Math.PI) * Math.asin(c / a);
    }
    return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c;
  }
  start();
})();
</script>



用戶評論
大牛,別默默的看了,快登錄幫我點評一下吧!:)      登錄 | 注冊


熱門標簽: 滑動選項卡 滑動切換 加載動畫 滾動切換 滾動條切換 滑動手風琴 選項卡切換 選項卡 切換 tab切換 頁面切換 選項卡插件 切換插件 滑動星星打分 h5彈窗動畫 html5彈窗動畫 旋轉翻轉 旋轉 翻轉 h5動畫 h5背景動畫 h5場景動畫 h53D動畫 h5界面動畫 html5動畫 其他 h5按鈕動畫 html5按鈕動畫 旋轉木馬 圖片旋轉木馬 文字旋轉木馬 旋轉木馬插件 背景切換 大圖切換 h5線條動畫 html5線條動畫 鼠標滑過 鼠標懸停 跟隨鼠標移動
?
×
×

注冊

官方QQ群

掃描上面二維碼加微信群

官方QQ群

jQuery/js討論群
群號:642649996
Css3+Html5討論群
群號:322131262

加群請備注:從官網了解到

老夫子电子