将div放在光标位置

时间:2015-12-28 21:57:47

标签: javascript css

我使用this color wheel,我想添加一个div作为拖动器。我能够实现它。问题是,当我在色轮上面添加东西时。拖拉机落后于色轮上方物体的高度

例如,如果我在色轮上方添加高度为50px的div,则拖动器将随光标一起移动,但它将在光标位置上方50px。我希望它位于光标的确切位置。

必须更改的相关代码是:

currentX = e.pageX - $(c).offset().left - radiusPlusOffset || currentX;
currentY = e.pageY - $(c).offset().top - radiusPlusOffset || currentY;

明显的解决方案'只是减去色轮上方div的高度。问题是,它不会是动态的。我必须为每个div添加高度。我认为offset()应该计算出来。

如何动态地将拖动器div放在光标的确切位置?我更喜欢用简单的JavaScript而不是JQuery来做这件事。

JSFiddle



var b = document.body;
var c = document.createElement('canvas');
var a = c.getContext('2d');
var wrapper = document.getElementById('wrapper');
document.body.clientWidth; // fix bug in webkit: http://qfox.nl/weblog/218

var colorWheelDiv = document.createElement('div');
var dragger = document.createElement('div');
dragger.id = 'dragger';

var otherDiv = document.createElement('div');
var text = document.createTextNode('Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, ');
colorWheelDiv.appendChild(c);
otherDiv.appendChild(text);
colorWheelDiv.appendChild(dragger);

wrapper.appendChild(otherDiv);
wrapper.appendChild(colorWheelDiv);

(function() {

  // Declare constants and variables to help with minification
  // Some of these are inlined (with comments to the side with the actual equation)
  var doc = document;
  doc.c = doc.createElement;
  b.a = b.appendChild;

  var width = c.width = c.height = 400,
    label = b.a(doc.c("p")),
    input = b.a(doc.c("input")),
    imageData = a.createImageData(width, width),
    pixels = imageData.data,
    oneHundred = input.value = input.max = 100,
    circleOffset = 0,
    diameter = width - circleOffset * 2,
    radius = diameter / 2,
    radiusPlusOffset = radius + circleOffset,
    radiusSquared = radius * radius,
    two55 = 255,
    currentY = oneHundred,
    currentX = -currentY,
    wheelPixel = circleOffset * 4 * width + circleOffset * 4;

  // Math helpers
  var math = Math,
    PI = math.PI,
    PI2 = PI * 2,
    sqrt = math.sqrt,
    atan2 = math.atan2;

  // Setup DOM properties
  b.style.textAlign = "center";
  label.style.font = "2em courier";
  input.type = "range";

  // Load color wheel data into memory.
  for (y = input.min = 0; y < width; y++) {
    for (x = 0; x < width; x++) {
      var rx = x - radius,
        ry = y - radius,
        d = rx * rx + ry * ry,
        rgb = hsvToRgb(
          (atan2(ry, rx) + PI) / PI2, // Hue
          sqrt(d) / radius, // Saturation
          1 // Value
        );

      // Print current color, but hide if outside the area of the circle
      pixels[wheelPixel++] = rgb[0];
      pixels[wheelPixel++] = rgb[1];
      pixels[wheelPixel++] = rgb[2];
      pixels[wheelPixel++] = d > radiusSquared ? 0 : two55;
    }
  }

  // Bind Event Handlers
  input.onchange = redraw;
  c.onmousedown = doc.onmouseup = function(e) {
    // Unbind mousemove if this is a mouseup event, or bind mousemove if this a mousedown event
    doc.onmousemove = /p/.test(e.type) ? 0 : (redraw(e), redraw);
  }

  // Handle manual calls + mousemove event handler + input change event handler all in one place.
  function redraw(e) {

    // Only process an actual change if it is triggered by the mousemove or mousedown event.
    // Otherwise e.pageX will be undefined, which will cause the result to be NaN, so it will fallback to the current value
    currentX = e.pageX - $(c).offset().left - radiusPlusOffset || currentX;
    currentY = e.pageY - $(c).offset().top - radiusPlusOffset || currentY;

    // Scope these locally so the compiler will minify the names.  Will manually remove the 'var' keyword in the minified version.
    var theta = atan2(currentY, currentX),
      d = currentX * currentX + currentY * currentY;

    // If the x/y is not in the circle, find angle between center and mouse point:
    //   Draw a line at that angle from center with the distance of radius
    //   Use that point on the circumference as the draggable location
    if (d > radiusSquared) {
      currentX = radius * math.cos(theta);
      currentY = radius * math.sin(theta);
      theta = atan2(currentY, currentX);
      d = currentX * currentX + currentY * currentY;
    }

    label.textContent = b.style.background = hsvToRgb(
      (theta + PI) / PI2, // Current hue (how many degrees along the circle)
      sqrt(d) / radius, // Current saturation (how close to the middle)
      input.value / oneHundred // Current value (input type="range" slider value)
    )[3];

    // Reset to color wheel and draw a spot on the current location. 
    a.putImageData(imageData, 0, 0);

    // Draw the current spot.
    // I have tried a rectangle, circle, and heart shape.
    /*
    // Rectangle:
    a.fillStyle = '#000';
    a.fillRect(currentX+radiusPlusOffset,currentY+radiusPlusOffset, 6, 6);
    */
    /*
    // Circle:
    a.beginPath();  
    a.strokeStyle = '#000';
    a.arc(~~currentX+radiusPlusOffset,~~currentY+radiusPlusOffset, 4, 0, PI2);
    a.stroke();
    */

    // Heart:
    /*a.font = "1em arial";
    a.fillText("♥", currentX + radiusPlusOffset - 4, currentY + radiusPlusOffset + 4);*/

    dragger.style.left = (~~currentX + radiusPlusOffset) + 'px';
    dragger.style.top = (~~currentY + radiusPlusOffset) + 'px';

  }

  // Created a shorter version of the HSV to RGB conversion function in TinyColor
  // https://github.com/bgrins/TinyColor/blob/master/tinycolor.js
  function hsvToRgb(h, s, v) {
    h *= 6;
    var i = ~~h,
      f = h - i,
      p = v * (1 - s),
      q = v * (1 - f * s),
      t = v * (1 - (1 - f) * s),
      mod = i % 6,
      r = [v, q, p, p, t, v][mod] * two55,
      g = [t, v, v, q, p, p][mod] * two55,
      b = [p, p, t, v, v, q][mod] * two55;

    return [r, g, b, "rgb(" + ~~r + "," + ~~g + "," + ~~b + ")"];
  }

  // Kick everything off
  redraw(0);

  /*
  // Just an idea I had to kick everything off with some changing colors…
  // Probably no way to squeeze this into 1k, but it could probably be a lot smaller than this:
  currentX = currentY = 1;
  var interval = setInterval(function() {
      currentX--;
      currentY*=1.05;
      redraw(0)
  }, 7);
    
  setTimeout(function() {
      clearInterval(interval)
  }, 700)
  */

})();
&#13;
canvas {
  cursor: pointer;
}
#dragger {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  display: block;
  position: absolute;
  border: 2px solid black;
}
&#13;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id='wrapper'>
</div>
&#13;
&#13;
&#13;

1 个答案:

答案 0 :(得分:0)

您需要将包含画布和拖动器的div的位置更改为相对位置。然后顶部偏移将是正确的。左偏移在计算中也是错误的,它应该是: e.pageX - $(colorWheelDiv).offset()。left

Updated Fiddle

var b = document.body;
var c = document.createElement('canvas');
var a = c.getContext('2d');
var wrapper = document.getElementById('wrapper');
document.body.clientWidth; // fix bug in webkit: http://qfox.nl/weblog/218

var colorWheelDiv = document.createElement('div');
var dragger = document.createElement('div');
dragger.id = 'dragger';

var otherDiv = document.createElement('div');
var text = document.createTextNode('Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, ');
colorWheelDiv.appendChild(c);
otherDiv.appendChild(text);
colorWheelDiv.appendChild(dragger);
colorWheelDiv.style.position = 'relative';

wrapper.appendChild(otherDiv);
wrapper.appendChild(colorWheelDiv);

(function() {

  // Declare constants and variables to help with minification
  // Some of these are inlined (with comments to the side with the actual equation)
  var doc = document;
  doc.c = doc.createElement;
  b.a = b.appendChild;

  var width = c.width = c.height = 400,
    label = b.a(doc.c("p")),
    input = b.a(doc.c("input")),
    imageData = a.createImageData(width, width),
    pixels = imageData.data,
    oneHundred = input.value = input.max = 100,
    circleOffset = 0,
    diameter = width - circleOffset * 2,
    radius = diameter / 2,
    radiusPlusOffset = radius + circleOffset,
    radiusSquared = radius * radius,
    two55 = 255,
    currentY = oneHundred,
    currentX = -currentY,
    wheelPixel = circleOffset * 4 * width + circleOffset * 4;

  // Math helpers
  var math = Math,
    PI = math.PI,
    PI2 = PI * 2,
    sqrt = math.sqrt,
    atan2 = math.atan2;

  // Setup DOM properties
  b.style.textAlign = "center";
  label.style.font = "2em courier";
  input.type = "range";

  // Load color wheel data into memory.
  for (y = input.min = 0; y < width; y++) {
    for (x = 0; x < width; x++) {
      var rx = x - radius,
        ry = y - radius,
        d = rx * rx + ry * ry,
        rgb = hsvToRgb(
          (atan2(ry, rx) + PI) / PI2, // Hue
          sqrt(d) / radius, // Saturation
          1 // Value
        );

      // Print current color, but hide if outside the area of the circle
      pixels[wheelPixel++] = rgb[0];
      pixels[wheelPixel++] = rgb[1];
      pixels[wheelPixel++] = rgb[2];
      pixels[wheelPixel++] = d > radiusSquared ? 0 : two55;
    }
  }

  // Bind Event Handlers
  input.onchange = redraw;
  c.onmousedown = doc.onmouseup = function(e) {
    // Unbind mousemove if this is a mouseup event, or bind mousemove if this a mousedown event
    doc.onmousemove = /p/.test(e.type) ? 0 : (redraw(e), redraw);
  }

  // Handle manual calls + mousemove event handler + input change event handler all in one place.
  function redraw(e) {

    // Only process an actual change if it is triggered by the mousemove or mousedown event.
    // Otherwise e.pageX will be undefined, which will cause the result to be NaN, so it will fallback to the current value
    currentX = e.pageX - $(c).offset().left - radiusPlusOffset || currentX;
    currentY = e.pageY - $(c).offset().top - radiusPlusOffset || currentY;

    // Scope these locally so the compiler will minify the names.  Will manually remove the 'var' keyword in the minified version.
    var theta = atan2(currentY, currentX),
      d = currentX * currentX + currentY * currentY;

    // If the x/y is not in the circle, find angle between center and mouse point:
    //   Draw a line at that angle from center with the distance of radius
    //   Use that point on the circumference as the draggable location
    if (d > radiusSquared) {
      currentX = radius * math.cos(theta);
      currentY = radius * math.sin(theta);
      theta = atan2(currentY, currentX);
      d = currentX * currentX + currentY * currentY;
    }

    label.textContent = b.style.background = hsvToRgb(
      (theta + PI) / PI2, // Current hue (how many degrees along the circle)
      sqrt(d) / radius, // Current saturation (how close to the middle)
      input.value / oneHundred // Current value (input type="range" slider value)
    )[3];

    // Reset to color wheel and draw a spot on the current location. 
    a.putImageData(imageData, 0, 0);

    // Draw the current spot.
    // I have tried a rectangle, circle, and heart shape.
    /*
    // Rectangle:
    a.fillStyle = '#000';
    a.fillRect(currentX+radiusPlusOffset,currentY+radiusPlusOffset, 6, 6);
    */
    /*
    // Circle:
    a.beginPath();  
    a.strokeStyle = '#000';
    a.arc(~~currentX+radiusPlusOffset,~~currentY+radiusPlusOffset, 4, 0, PI2);
    a.stroke();
    */

    // Heart:
    /*a.font = "1em arial";
    a.fillText("♥", currentX + radiusPlusOffset - 4, currentY + radiusPlusOffset + 4);*/

    dragger.style.left = (e.pageX - $(colorWheelDiv).offset().left) + 'px';
    dragger.style.top = (~~currentY + radiusPlusOffset) + 'px';

  }

  // Created a shorter version of the HSV to RGB conversion function in TinyColor
  // https://github.com/bgrins/TinyColor/blob/master/tinycolor.js
  function hsvToRgb(h, s, v) {
    h *= 6;
    var i = ~~h,
      f = h - i,
      p = v * (1 - s),
      q = v * (1 - f * s),
      t = v * (1 - (1 - f) * s),
      mod = i % 6,
      r = [v, q, p, p, t, v][mod] * two55,
      g = [t, v, v, q, p, p][mod] * two55,
      b = [p, p, t, v, v, q][mod] * two55;

    return [r, g, b, "rgb(" + ~~r + "," + ~~g + "," + ~~b + ")"];
  }

  // Kick everything off
  redraw(0);

  /*
  // Just an idea I had to kick everything off with some changing colors…
  // Probably no way to squeeze this into 1k, but it could probably be a lot smaller than this:
  currentX = currentY = 1;
  var interval = setInterval(function() {
      currentX--;
      currentY*=1.05;
      redraw(0)
  }, 7);

  setTimeout(function() {
      clearInterval(interval)
  }, 700)
  */

})();