]> git.r.bdr.sh - rbdr/heart/blobdiff - js/lib/heart_renderer.js
Update changelog
[rbdr/heart] / js / lib / heart_renderer.js
index fd4adf6452e511e7090b1b0e7519dd467ef6829b..19ff1374e40e99ba231cd39f3321e721ebab61e0 100644 (file)
@@ -3,9 +3,6 @@
 ((window) => {
 
   const kColorIteratorLimit = 256;
-  const kRedSpeed = 0.1;
-  const kGreenSpeed = 0.2;
-  const kBlueSpeed = 0.15;
 
   /**
    * Renders a Heart, has its own canvas, which will be placed in the element
        * @instance
        * @name heartSize
        * @type Number
-       * @default 50
+       * @default 40
        */
-      this.heartSize = 50;
+      this.heartSize = 40;
 
+      /**
+       * The max size of the heart as a percentage of the canvas smallest dimension
+       *
+       * @memberof HeartRenderer
+       * @instance
+       * @name maxHeartSize
+       * @type Number
+       * @default 75
+       */
+      this.maxHeartSize = 75;
+
+      /**
+       * The min size of the heart as a percentage of the canvas smallest dimension
+       *
+       * @memberof HeartRenderer
+       * @instance
+       * @name minHeartSize
+       * @type Number
+       * @default 10
+       */
+      this.minHeartSize = 10;
+
+      this._ticking = false; // Lock for wheel event.
+      this._resizeMagnitude = 0.1; // Multiplies the wheel delta to resize the heart
+      this._resizeSpeed = 1; // How many percent points per frame we'll resize to match target
+      this._trackingSpeed = 10; // How many pixels per frame will we move to match target
+      this._following = null; // The status of mouse follow.
+      this._center = null; // The actual center
+      this._targetHeartSize = this.heartSize;
+      this._
+      this._targetCenter = {
+        x: 0,
+        y: 0
+      }; // the target coordinates
       this._animating = false; // The status of the animation.
       this._previousFrameTime = Date.now(); // The timestamp of the last frame for fps control
+      this._cursorTimeout = 500; // Timeout to hide the cursor in milliseconds
       this._currentColor = { // The current color that will be painted
         red: 100,
         blue: 0,
         green: 50
       };
+      this._colorSpeed = {
+        red: 0.1,
+        blue: 0.2,
+        green: 0.15
+      };
+      this._colorDirection = {
+        red: 1,
+        blue: 1,
+        green: 1
+      };
+
+      this._detectWheel();
+      this.startFollowingMouse();
 
       Object.assign(this, config);
     }
     render(element) {
 
       element.appendChild(this.canvas);
+
+      this._targetCenter = {
+        x: Math.round(this.canvas.width / 2),
+        y: Math.round(this.canvas.height / 2)
+      }; // the target coordinates
       this.resize();
     }
 
      * @memberof HeartRenderer
      * @function render
      * @instance
-     * @param {HTMLElement} element the element where we will attach our canvas
      */
     resize() {
 
       }
     }
 
+    /**
+     * Follows the mouse
+     *
+     * @memberof HeartRenderer
+     * @function startFollowingMouse
+     * @instance
+     */
+    startFollowingMouse() {
+
+      if (!this._following) {
+        this._following = this._setCenterFromMouse.bind(this);
+        this.canvas.addEventListener('mousemove', this._following);
+      }
+    }
+
+    /**
+     * Stop following the mouse
+     *
+     * @memberof HeartRenderer
+     * @function stopFollowingMouse
+     * @instance
+     */
+    stopFollowingMouse() {
+
+      if (this._following) {
+        this.canvas.removeEventListener('mouseover', this._following);
+        this._following = null;
+        this._targetCenter = {
+          x: Math.round(this.canvas.width / 2),
+          y: Math.round(this.canvas.height / 2)
+        }; // the target coordinates
+      }
+    }
+
     /**
      * Gets the context from the current canvas and starts the animation process
      *
     // Updates the current color
     _updateColor(delta) {
 
-      this._currentColor.red = Math.round(this._currentColor.red + delta * kRedSpeed) % kColorIteratorLimit;
-      this._currentColor.green = Math.round(this._currentColor.green + delta * kGreenSpeed) % kColorIteratorLimit;
-      this._currentColor.blue = Math.round(this._currentColor.blue + delta * kBlueSpeed) % kColorIteratorLimit;
+      const red = this._updateColorComponent('red', delta);
+      const green = this._updateColorComponent('green', delta);
+      const blue = this._updateColorComponent('blue', delta);
+
+      console.log(red, green, blue);
+
+      this._currentColor.red = red;
+      this._currentColor.green = green;
+      this._currentColor.blue = blue;
+    }
+
+    // Updates a single color component.
+    _updateColorComponent(component, delta) {
+      let color = Math.round(this._currentColor[component] + (delta * this._colorSpeed[component] * this._colorDirection[component]));
+      if (color >= kColorIteratorLimit) {
+        this._colorDirection[component] = -1;
+        color = kColorIteratorLimit;
+      }
+
+      if (color <= 0) {
+        this._colorDirection[component] = 1;
+        color = 0;
+      }
+
+      return color;
     }
 
     // Draws a heart
 
       const referenceDimension = canvasWidth < canvasHeight ? canvasWidth : canvasHeight;
 
+      this.heartSize += Math.sign(this._targetHeartSize - this.heartSize) * this._resizeSpeed;
+
       const heartSize = Math.round(referenceDimension * this.heartSize * .01);
       const radius = heartSize / 2;
-      const canvasCenterX = Math.round(canvasWidth / 2);
-      const canvasCenterY = Math.round(canvasHeight / 2);
+
+      if (!this._center) {
+        this._center = {};
+        this._center.x = Math.round(canvasWidth / 2);
+        this._center.y = Math.round(canvasHeight / 2);
+      }
+
+      const deltaY = this._targetCenter.y - this._center.y;
+      const deltaX = this._targetCenter.x - this._center.x;
+      const angle = Math.atan2(deltaY, deltaX);
+
+      // Move towards the target
+      this._center.x += Math.cos(angle) * this._trackingSpeed;
+      this._center.y += Math.sin(angle) * this._trackingSpeed;
+
+      const canvasCenterX = this._center.x;
+      const canvasCenterY = this._center.y;
       const centerX = -radius;
       const centerY = -radius;
 
 
       context.setTransform(1, 0, 0, 1, 0, 0);
     }
+
+    // Sets the center from mouse
+    _setCenterFromMouse(event) {
+
+      this._showCursor();
+      this._targetCenter.x = event.offsetX;
+      this._targetCenter.y = event.offsetY;
+      setTimeout(this._hideCursor.bind(this), this._cursorTimeout);
+    }
+
+    // Binds the wheel event to resize the heart
+    _detectWheel() {
+
+      this.canvas.addEventListener('wheel', this._onWheel.bind(this));
+    }
+
+    // Handle the mouse wheel movement
+    _onWheel(event) {
+
+      if (!this._ticking) {
+        this._ticking = true;
+        window.requestAnimationFrame(this._resizeHeartFromDelta.bind(this, event.deltaY));
+      }
+    }
+
+    // Use delta to resize the heart
+    _resizeHeartFromDelta(delta) {
+
+      let heartSize = this.heartSize + (this._resizeMagnitude * delta);
+
+      if (heartSize > this.maxHeartSize) {
+        heartSize = this.maxHeartSize;
+      }
+
+      if (heartSize < this.minHeartSize) {
+        heartSize = this.minHeartSize;
+      }
+
+      this._targetHeartSize = heartSize;
+      this._ticking = false;
+    }
+
+    // Apply a class to show the cursor.
+    _showCursor() {
+      this.canvas.classList.add('mouse-moving');
+    }
+
+    // Remove class to hide the cursor.
+    _hideCursor() {
+      this.canvas.classList.remove('mouse-moving');
+    }
   };