使光标停留在边界

时间:2016-04-05 03:58:39

标签: javascript css

我有一个色轮选择器(我从this library中获取了大量代码)。我试图让色轮光标不会超出界限。我不希望它通过灰色边框。

我可以做到显而易见并制作父PrintWriterdiv,但这只会隐藏光标,它不会使它不能超越边界。

我认为要编辑的相关变量是以下之一(在overflow:hidden函数中,从第39行开始):

hsvMove

如何让光标不越过边界?

JSFiddle

var r = currentTargetHeight / 2,
x = e.pageX - startPoint.left - r,
y = e.pageY - startPoint.top - r
(function(window) {
  "use strict"

  // Some common use variables
  var myColor = new Colors(),
    startPoint,
    currentTarget,
    currentTargetHeight = 0,
    PI = Math.PI,
    PI2 = PI * 2;


  /* ---------------------------------- */
  /* ---- HSV-circle color picker ----- */
  /* ---------------------------------- */
  var colorDiskWrapper = document.getElementById('colorDiskWrapper'),
    colorDiskCursor = document.getElementById('colorDiskCursor'),
    colorDisk = document.getElementById('colorDisk');

  var colorDiscRadius = colorDisk.offsetHeight / 2;

  // Create Event Functions
  var hsvDown = function(e) { // mouseDown callback
      var target = e.target || e.srcElement;

      if (e.preventDefault) e.preventDefault();
      if (target === colorDiskCursor || target === colorDisk) {
        currentTarget = target.parentNode;
      } else
        return;

      startPoint = getOrigin(currentTarget);
      currentTargetHeight = currentTarget.offsetHeight;

      addEvent(window, 'mousemove', hsvMove);
      hsvMove(e);
      startRender();
    },
    hsvMove = function(e) { // mouseMove callback
      var r = currentTargetHeight / 2,
        x = e.pageX - startPoint.left - r,
        y = e.pageY - startPoint.top - r,
        h = 360 - ((Math.atan2(y, x) * 180 / PI) + (y < 0 ? 360 : 0)),
        s = (Math.sqrt((x * x) + (y * y)) / r) * 100;
      myColor.setColor({
        h: h,
        s: s
      }, 'hsv');
    },
    renderHSVPicker = function(color) { // used in renderCallback of 'new ColorPicker'
      var x = Math.cos(PI2 - color.hsv.h * PI2),
        y = Math.sin(PI2 - color.hsv.h * PI2),
        r = color.hsv.s * colorDiscRadius;

      // Position the Cursor
      colorDiskCursor.style.left = (x * r + colorDiscRadius) + 'px';
      colorDiskCursor.style.top = (y * r + colorDiscRadius) + 'px';
    };

  addEvent(colorDiskWrapper, 'mousedown', hsvDown); // event delegation
  addEvent(window, 'mouseup', function() {
    removeEvent(window, 'mousemove', hsvMove);
    stopRender();
  });


  var doRender = function(color) {
      renderHSVPicker(color);
    },
    renderTimer,
    startRender = function(oneTime) {
      if (oneTime) { // only Colors is instanciated
        doRender(myColor.colors);
      } else {
        renderTimer = window.setInterval(
          function() {
            doRender(myColor.colors);
            // http://stackoverflow.com/questions/2940054/
          }, 13); // 1000 / 60); // ~16.666 -> 60Hz or 60fps
      }
    },
    stopRender = function() {
      window.clearInterval(renderTimer);
    };



  /*
  Function Helpers
  */

  function getOrigin(elm) {
    var box = (elm.getBoundingClientRect) ? elm.getBoundingClientRect() : {
        top: 0,
        left: 0
      },
      doc = elm && elm.ownerDocument,
      body = doc.body,
      win = doc.defaultView || doc.parentWindow || window,
      docElem = doc.documentElement || body.parentNode,
      clientTop = docElem.clientTop || body.clientTop || 0, // border on html or body or both
      clientLeft = docElem.clientLeft || body.clientLeft || 0;

    return {
      left: box.left + (win.pageXOffset || docElem.scrollLeft) - clientLeft,
      top: box.top + (win.pageYOffset || docElem.scrollTop) - clientTop
    };
  }


  function addEvent(obj, type, func) {
    addEvent.cache = addEvent.cache || {
      _get: function(obj, type, func, checkOnly) {
        var cache = addEvent.cache[type] || [];

        for (var n = cache.length; n--;) {
          if (obj === cache[n].obj && '' + func === '' + cache[n].func) {
            func = cache[n].func;
            if (!checkOnly) {
              cache[n] = cache[n].obj = cache[n].func = null;
              cache.splice(n, 1);
            }
            return func;
          }
        }
      },
      _set: function(obj, type, func) {
        var cache = addEvent.cache[type] = addEvent.cache[type] || [];

        if (addEvent.cache._get(obj, type, func, true)) {
          return true;
        } else {
          cache.push({
            func: func,
            obj: obj
          });
        }
      }
    };

    if (!func.name && addEvent.cache._set(obj, type, func) || typeof func !== 'function') {
      return;
    }

    if (obj.addEventListener) obj.addEventListener(type, func, false);
    else obj.attachEvent('on' + type, func);
  }

  function removeEvent(obj, type, func) {
    if (typeof func !== 'function') return;
    if (!func.name) {
      func = addEvent.cache._get(obj, type, func) || func;
    }

    if (obj.removeEventListener) obj.removeEventListener(type, func, false);
    else obj.detachEvent('on' + type, func);
  }
})(window);
#colorDisk {
  background-image: url("http://i.imgur.com/tX5NbWs.png");
  width: 350px;
  height: 350px;
  background-repeat: no-repeat;
  cursor: pointer;
}
#colorDiskCursor {
  position: absolute;
  border: 2px solid black;
  border-radius: 50%;
  width: 9px;
  height: 9px;
  cursor: pointer;
}

1 个答案:

答案 0 :(得分:3)

<强>问题:

让我们首先概述算法,因此我们都清楚我们要做的事情:每次鼠标移动/点击,计算鼠标位置相对所代表的HS值到彩色光盘(HSV彩色系统)。定期将磁盘光标准确渲染到与HS值对应的位置。

我们需要注意以下几点:

  1. 我们用来计算颜色值的实际半径(特别是S)是彩色圆盘的半径减去光标的半径,因为我们想要阻止光标离开彩色磁盘的边界。在这种情况下,我们有175px - 6.5px,即168.5px

  2. 渲染光标时,我们设置其左上角位置。我们需要通过半径来偏移位置,以便我们的“手指指针”很好地出现在光标的中间。

  3. <强>解决方案:

    基于上述理解,解决方案很简单。

    您的代码存在问题,因为您使用的是整个光盘半径(175px),而没有考虑光盘光标的半径(6.5px)。

    您应该在代码中修复/考虑的一些事项:

    1. 您的currentTargetHeight是包装器(350px)的高度,然后将其减半以派生r。这看起来对我不对。你根本不应该关心包装器的尺寸。从代码中删除此变量。我们需要关注的值应为colorDiskRadiuscolorDiskCursorRadius

    2. 您的colorDiscCursor设置为position: absolute,但其偏移父级不是包装器,因为包装器不是定位元素。因此,我们为colorDiscCursor设置的左上角位置可能完全不可预测,具体取决于其实际父级在实际页面上的位置。要解决此问题,请将包装器设置为position: relative

    3. 我注意到您没有设置box-sizing(默认为content-box),这就是为什么您的光标实际上13px宽,尽管有width: 9px;同样适合身高。我个人喜欢使用box-sizing: border-box,因此当我必须进行像素精确计算时,我只需要查看CSS中的实际widthheight属性,而无需参考到paddingborder

    4. 次要问题:您有时在代码中使用disc,有时使用disk。为了理智,尝试将其标准化。

    5. <强> TL; DR

      这是实施1,2和4的小提琴:https://jsfiddle.net/hrnn9w9k/4/

      我没有包括3,因为它可能不是您的偏好,但我强烈推荐它。