Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  drag-zoom.js   Sprache: JAVA

 
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */


"use strict";

const { debounce } = require("resource://devtools/shared/debounce.js");
const { lerp } = require("resource://devtools/client/memory/utils.js");
const EventEmitter = require("resource://devtools/shared/event-emitter.js");

const LERP_SPEED = 0.5;
const ZOOM_SPEED = 0.01;
const TRANSLATE_EPSILON = 1;
const ZOOM_EPSILON = 0.001;
const LINE_SCROLL_MODE = 1;
const SCROLL_LINE_SIZE = 15;

/**
 * DragZoom is a constructor that contains the state of the current dragging and
 * zooming behavior. It sets the scrolling and zooming behaviors.
 *
 * @param  {HTMLElement} container description
 *         The container for the canvases
 */

function DragZoom(container, debounceRate, requestAnimationFrame) {
  EventEmitter.decorate(this);

  this.isDragging = false;

  // The current mouse position
  this.mouseX = container.offsetWidth / 2;
  this.mouseY = container.offsetHeight / 2;

  // The total size of the visualization after being zoomed, in pixels
  this.zoomedWidth = container.offsetWidth;
  this.zoomedHeight = container.offsetHeight;

  // How much the visualization has been zoomed in
  this.zoom = 0;

  // The offset of visualization from the container. This is applied after
  // the zoom, and the visualization by default is centered
  this.translateX = 0;
  this.translateY = 0;

  // The size of the offset between the top/left of the container, and the
  // top/left of the containing element. This value takes into account
  // the device pixel ratio for canvas draws.
  this.offsetX = 0;
  this.offsetY = 0;

  // The smoothed values that are animated and eventually match the target
  // values. The values are updated by the update loop
  this.smoothZoom = 0;
  this.smoothTranslateX = 0;
  this.smoothTranslateY = 0;

  // Add the constant values for testing purposes
  this.ZOOM_SPEED = ZOOM_SPEED;
  this.ZOOM_EPSILON = ZOOM_EPSILON;

  const update = createUpdateLoop(container, this, requestAnimationFrame);

  this.destroy = setHandlers(this, container, update, debounceRate);
}

module.exports = DragZoom;

/**
 * Returns an update loop. This loop smoothly updates the visualization when
 * actions are performed. Once the animations have reached their target values
 * the animation loop is stopped.
 *
 * Any value in the `dragZoom` object that starts with "smooth" is the
 * smoothed version of a value that is interpolating toward the target value.
 * For instance `dragZoom.smoothZoom` approaches `dragZoom.zoom` on each
 * iteration of the update loop until it's sufficiently close as defined by
 * the epsilon values.
 *
 * Only these smoothed values and the container CSS are updated by the loop.
 *
 * @param {HTMLDivElement} container
 * @param {Object} dragZoom
 *        The values that represent the current dragZoom state
 * @param {Function} requestAnimationFrame
 */

function createUpdateLoop(container, dragZoom, requestAnimationFrame) {
  let isLooping = false;

  function update() {
    const isScrollChanging =
      Math.abs(dragZoom.smoothZoom - dragZoom.zoom) > ZOOM_EPSILON;
    const isTranslateChanging =
      Math.abs(dragZoom.smoothTranslateX - dragZoom.translateX) >
        TRANSLATE_EPSILON ||
      Math.abs(dragZoom.smoothTranslateY - dragZoom.translateY) >
        TRANSLATE_EPSILON;

    isLooping = isScrollChanging || isTranslateChanging;

    if (isScrollChanging) {
      dragZoom.smoothZoom = lerp(
        dragZoom.smoothZoom,
        dragZoom.zoom,
        LERP_SPEED
      );
    } else {
      dragZoom.smoothZoom = dragZoom.zoom;
    }

    if (isTranslateChanging) {
      dragZoom.smoothTranslateX = lerp(
        dragZoom.smoothTranslateX,
        dragZoom.translateX,
        LERP_SPEED
      );
      dragZoom.smoothTranslateY = lerp(
        dragZoom.smoothTranslateY,
        dragZoom.translateY,
        LERP_SPEED
      );
    } else {
      dragZoom.smoothTranslateX = dragZoom.translateX;
      dragZoom.smoothTranslateY = dragZoom.translateY;
    }

    const zoom = 1 + dragZoom.smoothZoom;
    const x = dragZoom.smoothTranslateX;
    const y = dragZoom.smoothTranslateY;
    container.style.transform = `translate(${x}px, ${y}px) scale(${zoom})`;

    if (isLooping) {
      requestAnimationFrame(update);
    }
  }

  // Go ahead and start the update loop
  update();

  return function restartLoopingIfStopped() {
    if (!isLooping) {
      update();
    }
  };
}

/**
 * Set the various event listeners and return a function to remove them
 *
 * @param  {Object} dragZoom
 * @param  {HTMLElement} container
 * @param  {Function} update
 * @return {Function}  The function to remove the handlers
 */

function setHandlers(dragZoom, container, update, debounceRate) {
  const emitChanged = debounce(() => dragZoom.emit("change"), debounceRate);

  const removeDragHandlers = setDragHandlers(
    container,
    dragZoom,
    emitChanged,
    update
  );
  const removeScrollHandlers = setScrollHandlers(
    container,
    dragZoom,
    emitChanged,
    update
  );

  return function removeHandlers() {
    removeDragHandlers();
    removeScrollHandlers();
  };
}

/**
 * Sets handlers for when the user drags on the canvas. It will update dragZoom
 * object with new translate and offset values.
 *
 * @param  {HTMLElement} container
 * @param  {Object} dragZoom
 * @param  {Function} changed
 * @param  {Function} update
 */

function setDragHandlers(container, dragZoom, emitChanged, update) {
  const parentEl = container.parentElement;

  function startDrag() {
    dragZoom.isDragging = true;
    container.style.cursor = "grabbing";
  }

  function stopDrag() {
    dragZoom.isDragging = false;
    container.style.cursor = "grab";
  }

  function drag(event) {
    const prevMouseX = dragZoom.mouseX;
    const prevMouseY = dragZoom.mouseY;

    dragZoom.mouseX = event.clientX - parentEl.offsetLeft;
    dragZoom.mouseY = event.clientY - parentEl.offsetTop;

    if (!dragZoom.isDragging) {
      return;
    }

    dragZoom.translateX += dragZoom.mouseX - prevMouseX;
    dragZoom.translateY += dragZoom.mouseY - prevMouseY;

    keepInView(container, dragZoom);

    emitChanged();
    update();
  }

  parentEl.addEventListener("mousedown", startDrag);
  parentEl.addEventListener("mouseup", stopDrag);
  parentEl.addEventListener("mouseout", stopDrag);
  parentEl.addEventListener("mousemove", drag);

  return function removeListeners() {
    parentEl.removeEventListener("mousedown", startDrag);
    parentEl.removeEventListener("mouseup", stopDrag);
    parentEl.removeEventListener("mouseout", stopDrag);
    parentEl.removeEventListener("mousemove", drag);
  };
}

/**
 * Sets the handlers for when the user scrolls. It updates the dragZoom object
 * and keeps the canvases all within the view. After changing values update
 * loop is called, and the changed event is emitted.
 *
 * @param  {HTMLDivElement} container
 * @param  {Object} dragZoom
 * @param  {Function} changed
 * @param  {Function} update
 */

function setScrollHandlers(container, dragZoom, emitChanged, update) {
  const window = container.ownerDocument.defaultView;

  function handleWheel(event) {
    event.preventDefault();

    if (dragZoom.isDragging) {
      return;
    }

    // Update the zoom level
    const scrollDelta = getScrollDelta(event, window);
    const prevZoom = dragZoom.zoom;
    dragZoom.zoom = Math.max(0, dragZoom.zoom - scrollDelta * ZOOM_SPEED);

    // Calculate the updated width and height
    const prevZoomedWidth = container.offsetWidth * (1 + prevZoom);
    const prevZoomedHeight = container.offsetHeight * (1 + prevZoom);
    dragZoom.zoomedWidth = container.offsetWidth * (1 + dragZoom.zoom);
    dragZoom.zoomedHeight = container.offsetHeight * (1 + dragZoom.zoom);
    const deltaWidth = dragZoom.zoomedWidth - prevZoomedWidth;
    const deltaHeight = dragZoom.zoomedHeight - prevZoomedHeight;

    const mouseOffsetX = dragZoom.mouseX - container.offsetWidth / 2;
    const mouseOffsetY = dragZoom.mouseY - container.offsetHeight / 2;

    // The ratio of where the center of the mouse is in regards to the total
    // zoomed width/height
    const ratioZoomX =
      (prevZoomedWidth / 2 + mouseOffsetX - dragZoom.translateX) /
      prevZoomedWidth;
    const ratioZoomY =
      (prevZoomedHeight / 2 + mouseOffsetY - dragZoom.translateY) /
      prevZoomedHeight;

    // Distribute the change in width and height based on the above ratio
    dragZoom.translateX -= lerp(-deltaWidth / 2, deltaWidth / 2, ratioZoomX);
    dragZoom.translateY -= lerp(-deltaHeight / 2, deltaHeight / 2, ratioZoomY);

    // Keep the canvas in range of the container
    keepInView(container, dragZoom);
    emitChanged();
    update();
  }

  container.addEventListener("wheel", handleWheel);

  return function removeListener() {
    container.removeEventListener("wheel", handleWheel);
  };
}

/**
 * Account for the various mouse wheel event types, per pixel or per line
 *
 * @param  {WheelEvent} event
 * @return {Number} The scroll size in pixels
 */

function getScrollDelta(event) {
  if (event.deltaMode === LINE_SCROLL_MODE) {
    // Update by a fixed arbitrary value to normalize scroll types
    return event.deltaY * SCROLL_LINE_SIZE;
  }
  return event.deltaY;
}

/**
 * Keep the dragging and zooming within the view by updating the values in the
 * `dragZoom` object.
 *
 * @param  {HTMLDivElement} container
 * @param  {Object} dragZoom
 */

function keepInView(container, dragZoom) {
  const { devicePixelRatio } = container.ownerDocument.defaultView;
  const overdrawX = (dragZoom.zoomedWidth - container.offsetWidth) / 2;
  const overdrawY = (dragZoom.zoomedHeight - container.offsetHeight) / 2;

  dragZoom.translateX = Math.max(
    -overdrawX,
    Math.min(overdrawX, dragZoom.translateX)
  );
  dragZoom.translateY = Math.max(
    -overdrawY,
    Math.min(overdrawY, dragZoom.translateY)
  );

  dragZoom.offsetX =
    devicePixelRatio *
    ((dragZoom.zoomedWidth - container.offsetWidth) / 2 - dragZoom.translateX);
  dragZoom.offsetY =
    devicePixelRatio *
    ((dragZoom.zoomedHeight - container.offsetHeight) / 2 -
      dragZoom.translateY);
}

Messung V0.5
C=92 H=100 G=95

¤ Dauer der Verarbeitung: 0.23 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge