RGB滑块在更改值时摆动

时间:2015-12-18 06:47:18

标签: javascript jquery html css colors

我正在尝试实现自己的色轮选择器,我使用this color wheel作为基础。我(最后)成功添加了RGB滑块。因此,现在当您更改色轮颜色时,RGB滑块会动态变化,当您更改RGB滑块时,色轮也会更新颜色。

问题是,当我从RGB滑块滑动滑块时,其他滑块也会移动一点点。例如,如果我滑动绿色值,红色和蓝色值会稍微改变。

我不确定是什么问题。当我移动一个滑块时,如何让其他滑块不移动? (显然如果我没有在redraw()中设置滑块值,当我移动1个滑块时滑块不会改变,但我正试图找到核心问题。)

JSFiddle

var b = document.body;
var colorWheelDiv = document.getElementById('colorWheelDiv');
var colorWheel = document.createElement('canvas');
var colorWheelOverlay = document.createElement('div');

var a = colorWheel.getContext('2d');
var label = document.getElementById('label');
var input = document.getElementById('input');

var redInput = document.getElementById('red');
var greenInput = document.getElementById('green');
var blueInput = document.getElementById('blue');
var alphaInput = document.getElementById('alpha');
var rgbInput = document.getElementsByClassName('rgbInput');
document.body.clientWidth; // fix bug in webkit: http://qfox.nl/weblog/218

// Jquery Elements

var $redSlider = $('#red');
var $greenSlider = $('#green');
var $blueSlider = $('#blue');
var $alphaSlider = $('#alpha');


(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.colorWheel = doc.createElement;
  b.a = b.appendChild;


  // Add the colorWheel and the colorWheelOverlay
  colorWheelDiv.appendChild(colorWheelOverlay);
  colorWheelDiv.appendChild(colorWheel);
  colorWheelOverlay.id = 'colorWheelOverlay';
  colorWheel.id = 'colorWheel';


  var width = colorWheel.width = colorWheel.height = colorWheelDiv.clientHeight,
    imageData = a.createImageData(width, width),
    pixels = imageData.data,
    oneHundred = input.value = input.max = 100,
    circleOffset = 10,
    diameter = width - circleOffset * 2,
    radius = diameter / 2,
    radiusPlusOffset = radius + circleOffset,
    radiusSquared = radius * radius,
    two55 = 255,
    currentY = oneHundred,
    currentX = -currentY,
    center = radius / 2,
    wheelPixel = circleOffset * 4 * width + circleOffset * 4;

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

  // 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 = colorWheel_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.oninput = redraw;
  colorWheel.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);
  }

  $(".rgbInput").not($alphaSlider).slider({
    range: "max",
    min: 0,
    max: 255,
    value: 0,
    slide: function(event, ui) {
      redrawRGB();
    }
  });


  function redrawRGB() {
    var red = $('#red').slider('value');
    var green = $('#green').slider('value');
    var blue = $('#blue').slider('value');

    var hsv = colorWheel_rgbToHsv(red, green, blue);

    var newD = math.round(math.pow(radius * hsv.s, 2));
    var newTheta = (hsv.h * PI2) - PI;

    currentX = math.round(math.sqrt(newD) * math.cos(newTheta));
    currentY = math.round(math.sqrt(newD) * math.sin(newTheta));
    input.value = math.round(hsv.v * 100);

    redraw(0);
  }

  // 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 - colorWheelDiv.offsetLeft - colorWheel.offsetLeft - radiusPlusOffset || currentX;
    currentY = e.pageY - colorWheelDiv.offsetTop - colorWheel.offsetTop - 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;
    }

    var vValue = parseInt(input.value, 10);
    var rgb = colorWheel_hsvToRgb(
      (theta + PI) / PI2, // Current hue (how many degrees along the circle)
      sqrt(d) / radius, // Current saturation (how close to the middle)
      vValue / oneHundred // Current value (input type="range" slider value)
    )

    label.textContent = b.style.background = rgb[3];
    colorWheelOverlay.style.opacity = ((vValue + 100 - 15) - (vValue * 2)) / oneHundred;


    // Set slider Position \\

    $redSlider.slider("value", math.round(rgb[0]));
    $greenSlider.slider("value", math.round(rgb[1]));
    $blueSlider.slider("value", math.round(rgb[2]));



    // 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);*/

  }

  // Created a shorter version of the HSV to RGB conversion function in TinyColor
  // https://github.com/bgrins/TinyColor/blob/master/tinycolor.js
  function colorWheel_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(" + math.round(r) + "," + math.round(g) + "," + math.round(b) + ")"];
  }

  function colorWheel_rgbToHsv(r, g, b) {

    r = r / two55;
    g = g / two55;
    b = b / two55;

    var max = math.max(r, g, b),
      min = math.min(r, g, b);
    var h, s, v = max;

    var d = max - min;
    s = max === 0 ? 0 : d / max;

    if (max == min) {
      h = 0; // achromatic
    } else {
      switch (max) {
        case r:
          h = (g - b) / d + (g < b ? 6 : 0);
          break;
        case g:
          h = (b - r) / d + 2;
          break;
        case b:
          h = (r - g) / d + 4;
          break;
      }
      h /= 6;
    }
    return {
      h: h,
      s: s,
      v: v
    };
  }

  // Kick everything off
  redraw(0);

})();
#colorWheelDiv {
  width: 400px;
  height: 400px;
  position: relative;
}
#colorWheelOverlay {
  background-color: black;
  position: absolute;
  pointer-events: none;
}
#colorWheelDiv,
#colorWheelOverlay,
#colorWheel {
  border-radius: 50%;
}
#colorWheelOverlay,
#colorWheel {
  width: 100%;
  height: 100%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/themes/smoothness/jquery-ui.css">
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.js"></script>


R:
<div id="red" class="rgbInput"></div>
<br />G:
<div id="green" class="rgbInput"></div>
<br />B:
<div id="blue" class="rgbInput"></div>
<br />A:
<div id="alpha" class="rgbInput"></div>
<br />

<div id=colorWheelDiv></div>
<p id='label' style="font-style: normal; font-variant: normal; font-weight: normal; font-stretch: normal; font-size: 2em; line-height: normal; font-family: courier;">rgb(239,183,131)</p>
<input id="input" max="100" type="range" min="0">

更新

我尝试了这个答案,但没有用。我仍然得到相同的结果。标题可能相同,但我不认为实际问题是相同的

1 个答案:

答案 0 :(得分:1)

您正在将RGB值转换为HSV,然后将它们转换回RGB以设置其他滑块值。转换过程具有有限的精度,因此您可以看到其他滑块的摆动。解决方案是直接从原始RGB值设置RGB滑块值,而不是从计算的HSV值设置。

更新

您可以按如下方式完成此操作:

// Set slider Position \\
if(e){
$redSlider.slider( "value", math.round(rgb[0]));
$greenSlider.slider( "value", math.round(rgb[1]));
$blueSlider.slider( "value", math.round(rgb[2]));
}