gojs中ZoomSlider放大缩小组件的坑


一. 需求

在gojs的画布上使用ZoomSlider放大缩小组件。

二. 问题

使用elementui中的Tabs切换标签,不同标签中的ZoomSlider按钮位置错乱。

三. 排查源码

发现源码根据ID来获取元素,但在VUE中会导致ID不唯一,所以按钮位置错乱
image
image

四. 解决问题

创建元素时不使用ID,使用class
image
并将所有获取方式改为通过类名获取
image

五. 优化

  1. 放出realign()方法,当画布大小变更时,可使用该方法重置放大缩小组件位置
    image
  2. 将组件放到画布里,以免定位不到
    image
  3. 放到画布后,按钮位置代码优化
    image

六. 代码

/* eslint-disable no-undef */
'use strict';
/*
*  Copyright (C) 1998-2021 by Northwoods Software Corporation. All Rights Reserved.
*/

/**
 * This class implements a zoom slider for GoJS diagrams.
 * The constructor takes two arguments:
 *   - `diagram` ***Diagram*** a reference to a GoJS Diagram
 *   - `options` ***Object*** an optional JS Object describing options for the slider
 *
 * Options:
 *   - `alignment` ***Spot*** see {@link #alignment}
 *   - `alignmentFocus` ***Spot*** see {@link #alignmentFocus}
 *   - `size` ***number*** see {@link #size}
 *   - `buttonSize` ***number*** see {@link #buttonSize}
 *   - `orientation` ***string*** see {@link #orientation}
 *   - `opacity` ***number*** see {@link #opacity}
 *
 * Example usage of ZoomSlider:
 * ```js
 * var zoomSlider = new ZoomSlider(myDiagram,
 *   {
 *     alignment: go.Spot.TopRight, alignmentFocus: go.Spot.TopRight,
 *     size: 150, buttonSize: 30, orientation: 'horizontal'
 *   });
 * ```
 *
 * This is the basic HTML Structure that the ZoomSlider creates as a sibling div of the diagram:
 * ```html
 * <div class="zoomSlider">
 *   <button id="zoomSliderOut" class="zoomButton">-</button>
 *   <div id="zoomSliderRangeCtn" class="zoomRangeContainer">
 *     <input id="zoomSliderRange" class="zoomRangeInput" type="range" min="-50" max="100">
 *   </div>
 *   <button id="zoomSliderIn" class="zoomButton">+</button>
 * </div>
 * ```
 *
 * <p class="box">
 * The diagram div's parent element should use `position: relative` to ensure the slider gets positioned properly.
 *
 * @constructor
 * @class
 * @param {Diagram} diagram the Diagram which the slider zooms in/out
 * @param {Object} options the options for the slider
 */
export default function ZoomSlider(diagram, options) {
  this._diagram = diagram;
  this._initialScale = diagram.scale;
  this._diagramDiv = diagram.div;
  this._sliderDiv = null;

  // Options defaults:
  this._size = 125;
  this._buttonSize = 25;
  this._alignment = go.Spot.BottomRight;
  this._alignmentFocus = go.Spot.BottomRight;
  this._orientation = 'vertical';
  this._opacity = .75;

  // Set properties based on options
  if (options !== undefined) {
    if (options['size'] !== undefined) this._size = options['size'];
    if (options['buttonSize'] !== undefined) this._buttonSize = options['buttonSize'];
    if (options['alignment'] !== undefined) this._alignment = options['alignment'];
    if (options['alignmentFocus'] !== undefined) this._alignmentFocus = options['alignmentFocus'];
    if (options['orientation'] !== undefined) this._orientation = options['orientation'];
    if (options['opacity'] !== undefined) this._opacity = options['opacity'];
  }

  // Prepare change listeners
  var self = this;
  this.updateOnViewportBoundsChanged = function () { self.scaleToValue(); };
  this.updateRealign = function() { self.realign() };

  this.init();
}

// Public properties

/**
 * This read-only property returns the diagram for which the slider is handling zoom.
 * @name ZoomSlider#diagram
 
 * @return {Diagram}
 */
Object.defineProperty(ZoomSlider.prototype, 'diagram', {
  get: function() { return this._diagram; }
});

/**
 * Gets or sets the overall length, in pixels, that the slider will occupy.
 * The default value is 125.
 * @name ZoomSlider#size
 
 * @return {number}
 */
Object.defineProperty(ZoomSlider.prototype, 'size', {
  get: function() { return this._size; },
  set: function(val) {
    var old = this._size;
    if (old !== val) {
      this._size = val;
      this.resize();
    }
  }
});

/**
 * Gets or sets the height/width of the buttons at each end of the slider.
 * The default value is 25.
 * @name ZoomSlider#buttonSize
 
 * @return {number}
 */
Object.defineProperty(ZoomSlider.prototype, 'buttonSize', {
  get: function() { return this._buttonSize; },
  set: function(val) {
    var old = this._buttonSize;
    if (old !== val) {
      this._buttonSize = val;
      this.resize();
    }
  }
});

/**
 * Gets or sets the alignment Spot of this slider to determine where it should be placed relative to the diagram.
 * The default value is Spot.BottomRight.
 * @name ZoomSlider#alignment
 
 * @return {Spot}
 */
Object.defineProperty(ZoomSlider.prototype, 'alignment', {
  get: function() { return this._alignment; },
  set: function(val) {
    var old = this._alignment;
    if (old !== val) {
      this._alignment = val;
      this.realign();
    }
  }
});

/**
 * Gets or sets the Spot on this slider to be used as the alignment point when placing it relative to the diagram.
 * The default value is Spot.BottomRight.
 * @name ZoomSlider#alignmentFocus
 
 * @return {Spot}
 */
Object.defineProperty(ZoomSlider.prototype, 'alignmentFocus', {
  get: function() { return this._alignmentFocus; },
  set: function(val) {
    var old = this._alignmentFocus;
    if (old !== val) {
      this._alignmentFocus = val;
      this.realign();
    }
  }
});

/**
 * Gets or sets whether the slider is oriented vertically or horizontally.
 * Must be either 'horizontal' or 'vertical' and is case-sensitive.
 * The default value is `'vertical'`.
 * @name ZoomSlider#orientation
 
 * @return {string}
 */
Object.defineProperty(ZoomSlider.prototype, 'orientation', {
  get: function() { return this._orientation; },
  set: function(val) {
    if (val !== 'horizontal' && val !== 'vertical') {
      throw new Error('Orientation must be "horizontal" or "vertical"');
    }
    var old = this._orientation;
    if (old !== val) {
      this._orientation = val;
      this.resize(true);
    }
  }
});

/**
 * Gets or sets the opacity of the slider.
 * The default value is 0.75.
 * @name ZoomSlider#opacity
 
 * @return {Spot}
 */
Object.defineProperty(ZoomSlider.prototype, 'opacity', {
  get: function() { return this._opacity; },
  set: function(val) {
    var old = this._opacity;
    if (old !== val) {
      this._opacity = val;
      this._sliderDiv.style.opacity = val;
    }
  }
});

/**
 * @ignore
 * Initialize the slider.
 * @this {ZoomSlider}
 */
ZoomSlider.prototype.init = function() {
  // Sets up the slider div and inner div's basic attributes and ids
  this.sliderDivSetup();
  this.resize(true);

  // Set up the runtime code
  this.sliderListenerSetup();
};

/**
 * @ignore
 * Create the necessary divs for the slider and add the slider as a sibling of the diagram.
 * @this {ZoomSlider}
 */
ZoomSlider.prototype.sliderDivSetup = function() {
  this._sliderDiv = document.createElement('div');
  this._sliderDiv.className = 'zoomSlider';

  // Initialize buttons and range input
  var zoomOutBtn = document.createElement('button');
  zoomOutBtn.className = 'zoomSliderOut zoomButton';
  zoomOutBtn.innerHTML = '-';
  this._sliderDiv.appendChild(zoomOutBtn);

  var zoomRangeContainer = document.createElement('div');
  zoomRangeContainer.className = 'zoomSliderRangeCtn zoomRangeContainer';
  this._sliderDiv.appendChild(zoomRangeContainer);

  var zoomRangeInput = document.createElement('input');
  zoomRangeInput.className = 'zoomSliderRange zoomRangeInput';
  zoomRangeInput.type = 'range';
  zoomRangeInput.min = -50;
  zoomRangeInput.max = 100;
  zoomRangeContainer.appendChild(zoomRangeInput);

  var zoomInBtn = document.createElement('button');
  zoomInBtn.className = 'zoomSliderIn zoomButton';
  zoomInBtn.innerHTML = '+';
  this._sliderDiv.appendChild(zoomInBtn);

  // Adds the slider as a sibling of the diagram
  // IMPORTANT: the diagram div's parent element should use position: relative
  if (this._diagramDiv !== null) {
    var diagramParent = this._diagramDiv;
    if (diagramParent !== null) {
      diagramParent.appendChild(this._sliderDiv);
    }
  }
};

/**
 * @ignore
 * Add listeners to the buttons and range input. todoczl
 * Add a diagram listener
 * @this {ZoomSlider}
 */
ZoomSlider.prototype.sliderListenerSetup = function() {
  var diagramParent = this._diagramDiv.parentElement;
  var zoomOutBtn = diagramParent.getElementsByClassName('zoomSliderOut')[0];
  var zoomInBtn = diagramParent.getElementsByClassName('zoomSliderIn')[0];
  var zoomRangeInput = diagramParent.getElementsByClassName('zoomSliderRange')[0];

  if (zoomOutBtn === null || zoomInBtn === null || zoomRangeInput === null) return;

  // Set up diagram listener so the slider can be kept in sync with the diagram's scale
  this.diagram.addDiagramListener('ViewportBoundsChanged', this.updateOnViewportBoundsChanged);

  // Set up event handlers for buttons and input range slider
  var self = this;
  zoomOutBtn.onclick = function() {
    zoomRangeInput.stepDown();
    self.valueToScale();
  };

  zoomInBtn.onclick = function() {
    zoomRangeInput.stepUp();
    self.valueToScale();
  };

  var valChanged = function() {
    self.valueToScale();
  };
  zoomRangeInput.oninput = valChanged;
  zoomRangeInput.onchange = valChanged;
};

/**
 * @ignore
 * Resize the slider.
 * @this {ZoomSlider}
 * @param {boolean} reorient whether or not to reorient the slider/buttons
 */
ZoomSlider.prototype.resize = function(reorient) {
  var sliderWidth = 0;
  var sliderHeight = 0;

  var diagramParent = this._diagramDiv.parentElement;
  var zoomOutBtn = diagramParent.getElementsByClassName('zoomSliderOut')[0];
  var zoomInBtn = diagramParent.getElementsByClassName('zoomSliderIn')[0];
  var zoomRangeContainer = diagramParent.getElementsByClassName('zoomSliderRangeCtn')[0];
  var zoomRangeInput = diagramParent.getElementsByClassName('zoomSliderRange')[0];

  if (this._sliderDiv === null || zoomOutBtn === null || zoomInBtn === null ||
    zoomRangeContainer === null || zoomRangeInput === null) return;

  if (this.orientation === 'horizontal') {
    sliderWidth = this.size;
    sliderHeight = this.buttonSize;
    var rangeWidth = sliderWidth - sliderHeight * 2;

    zoomOutBtn.style.width = sliderHeight + 'px';
    zoomOutBtn.style.height = sliderHeight + 'px';

    zoomRangeContainer.style.width = rangeWidth + 'px';
    zoomRangeContainer.style.height = sliderHeight + 'px';

    zoomRangeInput.style.width = rangeWidth + 'px';
    zoomRangeInput.style.height = sliderHeight + 'px';
    zoomRangeInput.style.transformOrigin = null;

    zoomInBtn.style.width = sliderHeight + 'px';
    zoomInBtn.style.height = sliderHeight + 'px';
  } else {
    sliderHeight = this.size;
    sliderWidth = this.buttonSize;
    var rangeHeight = sliderHeight - sliderWidth * 2;

    zoomInBtn.style.width = sliderWidth + 'px';
    zoomInBtn.style.height = sliderWidth + 'px';

    zoomRangeContainer.style.width = sliderWidth + 'px';
    zoomRangeContainer.style.height = rangeHeight + 'px';

    zoomRangeInput.style.width = rangeHeight + 'px';
    zoomRangeInput.style.height = sliderWidth + 'px';
    zoomRangeInput.style.transformOrigin = rangeHeight / 2 + 'px ' + rangeHeight / 2 + 'px';

    zoomOutBtn.style.width = sliderWidth + 'px';
    zoomOutBtn.style.height = sliderWidth + 'px';
  }
  this._sliderDiv.style.width = sliderWidth + 'px';
  this._sliderDiv.style.height = sliderHeight + 'px';

  // Reorient the slider, if necessary
  if (reorient) {
    this.reorient();
  }

  // Realign based on new size
  this.realign();
}

/**
 * @ignore
 * Reorient the slider, changing the transform and the order of the buttons within the div. todoczl
 * @this {ZoomSlider}
 */
ZoomSlider.prototype.reorient = function() {

  var diagramParent = this._diagramDiv.parentElement;
  var zoomOutBtn = diagramParent.getElementsByClassName('zoomSliderOut')[0];
  var zoomInBtn = diagramParent.getElementsByClassName('zoomSliderIn')[0];
  var zoomRangeInput = diagramParent.getElementsByClassName('zoomSliderRange')[0];

  if (this._sliderDiv === null || zoomOutBtn === null || zoomInBtn === null || zoomRangeInput === null) return;

  // Need to set the transform of the range input and move the buttons to the correct sides
  if (this.orientation === 'horizontal') {
    zoomRangeInput.style.transform = null;
    this._sliderDiv.insertBefore(zoomOutBtn, this._sliderDiv.firstChild);
    this._sliderDiv.appendChild(zoomInBtn);
  } else {
    zoomRangeInput.style.transform = 'rotate(-90deg)';
    this._sliderDiv.insertBefore(zoomInBtn, this._sliderDiv.firstChild);
    this._sliderDiv.appendChild(zoomOutBtn);
  }
}

/**
 * @ignore
 * Realigns to slider relative to the diagram.
 * @this {ZoomSlider}
 */
ZoomSlider.prototype.realign = function() {
  if (this._diagramDiv === null || this._sliderDiv === null) return;

  var sliderWidth = 0;
  var sliderHeight = 0;
  if (this.orientation === 'horizontal') {
    sliderWidth = this.size;
    sliderHeight = this.buttonSize;
  } else {
    sliderHeight = this.size;
    sliderWidth = this.buttonSize;
  }

  // Finds the diagram and diagram's parent in the page
  var diagramParent = this._diagramDiv.parentElement;
  // var diagramLoc = this._diagramDiv.getBoundingClientRect();
  if (diagramParent !== null) {
    // var parentLoc = diagramParent.getBoundingClientRect();
    var top = 
      // diagramLoc.top - parentLoc.top +
      this.alignment.y * this._diagramDiv.clientHeight + this.alignment.offsetY -
      this.alignmentFocus.y * sliderHeight + this.alignmentFocus.offsetY;
    
    var left = 
      // diagramLoc.left - parentLoc.left +
      this.alignment.x * this._diagramDiv.clientWidth + this.alignment.offsetX -
      this.alignmentFocus.x * sliderWidth + this.alignmentFocus.offsetX;
    this._sliderDiv.style.top = top + 'px';
    this._sliderDiv.style.left = left + 'px';
  }
}

/**
 * @ignore
 * Update the value of the slider input to match the diagram's scale.
 * @this {ZoomSlider}
 */
ZoomSlider.prototype.scaleToValue = function() {
  var diagramParent = this._diagramDiv.parentElement;
  var slider = diagramParent.getElementsByClassName('zoomSliderRange')[0];
  var diagram = this.diagram;
  var A = this._initialScale;
  var B = diagram.commandHandler.zoomFactor;
  var y1 = diagram.scale;
  slider.value = Math.round(Math.log(y1/A) / Math.log(B)).toString();
}

/**
 * @ignore
 * Update the diagram's scale to match the value of the slider input.
 * @this {ZoomSlider}
 */
ZoomSlider.prototype.valueToScale = function() {
  var diagramParent = this._diagramDiv.parentElement;
  var slider = diagramParent.getElementsByClassName('zoomSliderRange')[0];
  var diagram = this.diagram;
  var x = parseFloat(slider.value);
  var A = this._initialScale;
  var B = diagram.commandHandler.zoomFactor;
  diagram.scale = A * Math.pow(B, x);
}

/**
 * Remove the slider from the page.
 * @this {ZoomSlider}
 */
ZoomSlider.prototype.remove = function() {
  // Remove the listener attached to diagram
  this.diagram.removeDiagramListener('ViewportBoundsChanged', this.updateOnViewportBoundsChanged);

  if (this._sliderDiv !== null) {
    this._sliderDiv.innerHTML = '';
    if (this._sliderDiv.parentElement) {
      this._sliderDiv.parentElement.removeChild(this._sliderDiv);
    }
    this._sliderDiv = null;
  }
};


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM