2016-08-26 12 views
8

Tôi có một canvas gradient HSV raindow khi bạn nhấp vào nó, phần tử được thêm tại vị trí đó với nền của nó làm màu của pixel được nhấp.Tính vị trí X, Y pixel trên gradient dựa trên màu hex bằng cách sử dụng Javascript

Điều tôi muốn là để nó hoạt động ngược lại. Ví dụ: nếu bạn có màu hex, tôi muốn tìm pixel đó trên canvas và tạo phần tử ở vị trí đó.

Suy nghĩ đầu tiên của tôi là bằng cách nào đó sử dụng hệ thống ma trận/góc phần tư. Suy nghĩ tiếp theo của tôi là kể từ khi tôi đang sử dụng HSV, tôi có thể sử dụng điểm vị trí gradient HSV của tôi để tìm ra vị trí. Vấn đề là điểm của tôi không đồng nhất với nhau khiến cho việc này khó hơn. Ngày đầu đó, tôi có một gradient màu trắng và màu đen gradient bao gồm gradient màu chính và tôi cần phải được tính toán.

Vì vậy, câu hỏi của tôi là, làm cách nào tôi có thể tìm vị trí của pixel màu hoặc ít nhất là khớp gần nhất bằng cách sử dụng mã hex?

Đây là mã của tôi vậy, đến nay: http://codepen.io/shelbywhite/pen/EyqPWY?editors=1000

HTML:

<div class="container"> 
    <canvas class="colorSpectrum"></canvas> 
    <div class="circle"></div> 
</div> 

CSS:

.container { 
    background: grey; 
    height: 350px; 
    width: 400px; 
} 

.circle { 
    background: transparent; 
    box-shadow: 0 0 8px rgba(0,0,0,0.2); 
    border-radius: 50%; 
    border: 2px solid #fff; 
    height: 20px; 
    margin: -12px; 
    width: 20px; 
    position: absolute; 
} 

.colorSpectrum { 
    display: block; 
    height: 100%; 
    transform: translateZ(0); 
    width: 100%; 
} 

Javascript:

$(function() { 

    var closest = function(num, arr) { 
     var curr = arr[0]; 
     var diff = Math.abs(num - curr); 

     for (var val = 0; val < arr.length; val++) { 
      var newdiff = Math.abs(num - arr[val]); 
      if (newdiff < diff) { 
       diff = newdiff; 
       curr = arr[val]; 
      } 
     } 

     return curr; 
    }; 


    var container = $('.container'); 
    var containerWidth = container.width(); 
    var containerHeight = container.height(); 

    var verticalGradientsHeight = Math.round(containerHeight * .34); 
    console.log('verticalGradientsHeight', verticalGradientsHeight); 
    var round = function(value, decimals) { 
     return Number(Math.round(value+'e'+decimals)+'e-'+decimals); 
    }; 


    // Draws the color spectrum onto the canvas 
    var drawColorSpectrum = function() { 

     // Cache canvas element 
     var canvasElement = $('.colorSpectrum'); 

     // Cache javascript element 
     var canvas = canvasElement[0]; 

     // Get canvas context 
     var ctx = canvas.getContext('2d'); 

     // Cache page height 
     var canvasWidth = containerWidth; 

     // Cache page height 
     var canvasHeight = containerHeight - 72; 

     // Bottom gradient start position 
     var blackStartYPos = canvasHeight - verticalGradientsHeight; 

     // Bottom gradient end position 
     var blackEndYPos = canvasHeight; 

     // Create white gradient element 
     var white = ctx.createLinearGradient(0, 0, 0, verticalGradientsHeight); 

     // Create black gradient element 
     var black = ctx.createLinearGradient(0, blackStartYPos, 0, blackEndYPos); 

     // Create new instance of image 
     var img = new Image(); 


     // Cache container 
     _colorSpectrumContainer = canvasElement.parent(); 

     // Set global var 
     spectrumCanvas = canvasElement; 

     // Set width of canvas 
     canvas.width = canvasWidth; 

     // Set height of canvas 
     canvas.height = canvasHeight; 

     // Image load listener 
     img.onload = function() { 

      // Draw intial image 
      ctx.drawImage(this, 0, 0, canvasWidth, canvasHeight); 

      // Draw white to transparent gradient 
      white.addColorStop(0, "hsla(0,0%,100%,1)"); 
      white.addColorStop(0.05, "hsla(0,0%,100%,1)"); 
      white.addColorStop(0.20, "hsla(0,0%,100%,0.89)"); 
      white.addColorStop(0.38, "hsla(0,0%,100%,0.69)"); 
      white.addColorStop(0.63, "hsla(0,0%,100%,0.35)"); 
      white.addColorStop(0.78, "hsla(0,0%,100%,0.18)"); 
      white.addColorStop(0.91, "hsla(0,0%,100%,0.06)"); 
      white.addColorStop(1, "hsla(0,0%,100%,0)"); 
      ctx.fillStyle = white; 
      ctx.fillRect(0, 0, canvasWidth, verticalGradientsHeight); 

      // Draw black to transparent gradient 
      black.addColorStop(0, "hsla(0,0%,0%,0)"); 
      black.addColorStop(0.20, "hsla(0,0%,0%,0.01)"); 
      black.addColorStop(0.28, "hsla(0,0%,0%,0.04)"); 
      black.addColorStop(0.35, "hsla(0,0%,0%,0.09)"); 
      black.addColorStop(0.51, "hsla(0,0%,0%,0.26)"); 
      black.addColorStop(0.83, "hsla(0,0%,0%,0.69)"); 
      black.addColorStop(1, "hsla(0,0%,0%,1)"); 

      ctx.fillStyle = black; 
      ctx.fillRect(0, blackStartYPos, canvasWidth, verticalGradientsHeight); 
     } 

     // Set image source 
     img.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAAABCAYAAACbv+HiAAAA0ElEQVR4AYWSh2oDMAwFz6u7//+d2YmXalGBIBM47nnPIIEtmd8FGBTgDbPxDmbn49pX+cZX+Nz4mkZ2SECEAXTCAprlalntBC5whdUJnOfKEy5DjZYtB+o0D3XUMk0tkaZZEn2VuyiJQQQywS/P4c25ucTrfF3ndsoVdjmy3NMiuptR1eHfNcBFM2orW1ZXru00JZiBDrIII5AG5AlloX5TcG6/ywuuv0zAbyL4TWRZmIvU5TNBTjCPIIu5N3YgO7Wxtbot3q4+2LgTyFnZ/QHzBZD1KDpyqQAAAABJRU5ErkJggg=="; 
    }; 


    // 
    var hexToRgb = function(hex) { 

     hex = hex.replace('#',''); 

     r = parseInt(hex.substring(0, 2), 16); 
     g = parseInt(hex.substring(2, 4), 16); 
     b = parseInt(hex.substring(4, 6), 16); 

     return [r, g, b]; 
    }; 


    // 
    var rgbToHsb = function(r, g, b) { 

     var rr, gg, bb, 
      r = r/255, 
      g = g/255, 
      b = b/255, 
      h, s, 
      v = Math.max(r, g, b), 
      diff = v - Math.min(r, g, b), 
      diffc = function(c){ 
       return (v - c)/6/diff + 1/2; 
      }; 

     if (diff == 0) { 
      h = s = 0; 
     } else { 
      s = diff/v; 
      rr = diffc(r); 
      gg = diffc(g); 
      bb = diffc(b); 

      if (r === v) { 
       h = bb - gg; 
      }else if (g === v) { 
       h = (1/3) + rr - bb; 
      }else if (b === v) { 
       h = (2/3) + gg - rr; 
      } 
      if (h < 0) { 
       h += 1; 
      }else if (h > 1) { 
       h -= 1; 
      } 
     } 

     return { 
      h: Math.round(h * 360), 
      s: Math.round(s * 100), 
      b: Math.round(v * 100) 
     }; 
    }; 


    // Find hue in stop range 
    var findHueInStopRange = function(hue) { 

     // Array of hue stops with HSV, RGB, and HEX info 
     var stops = [{ 
      h: 0, 
      l: 0, 
      s: 100, 
      b: 100 
     }, { 
      h: 60, 
      l: 21, 
      s: 100, 
      b: 100 
     }, { 
      h: 120, 
      l: 40, 
      s: 85, 
      b: 85 
     }, { 
      h: 180, 
      l: 56, 
      s: 85, 
      b: 85 
     }, { 
      h: 237, 
      l: 72, 
      s: 86, 
      b: 96 
     }, { 
      h: 300, 
      l: 89, 
      s: 86, 
      b: 96 
     }, { 
      h: 359, 
      l: 100, 
      s: 100, 
      b: 100 
     }]; 

     // Total number of stops 
     var stopsLength = stops.length; 

     // Loop through stops 
     for (var i = 0; i < stopsLength; i += 1) { 

      // Temp set 
      var currentStop = stops[i]; 

      // Temp set 
//    var nextStop = stops[i + 1]; 
      var nextStop = (i + 1 > stopsLength - 1) ? currentStop : stops[i + 1]; 

      // Location is a percentage 
      var huePos; 

      // Temp set 
      var xPos = false; 

      console.log('hue', currentStop.h, '>>', hue, '<<', nextStop.h); 
      // Find which range of hue stops the current color is 
      // Hue is between current and next hue stop 
      if (hue >= currentStop.h && hue <= nextStop.h) { 

       // hue is current stop 
       if (hue === currentStop.h) { 

        // Set as location 
        huePos = currentStop.l; 

        // hue is next stop 
       } else if (hue === nextStop.h) { 

        // Set as location 
        huePos = nextStop.l; 

       // Hue is somewhere between stops 
       } else { 

        // Get percentage location between hue stops 
        var relativeHuePos = (hue - currentStop.h)/(nextStop.h - currentStop.h); 

        // Normalized to fit custom gradient stop locations 
        huePos = relativeHuePos * (nextStop.l - currentStop.l) + currentStop.l; 
       } 

       // A location was found 
       if (huePos) { 

        // Convert from percentage to pixel position 
        xPos = Math.round(containerWidth * (huePos/100)); 

        return xPos; 

       } else { 

        continue; 
       } 
      } 
     } 
    }; 


    // Find saturation in stop range 
    var findSaturationInStopRange = function (saturation) { 

     // Array of hue stops with HSV, RGB, and HEX info 
     var stops = [{ 
      l: 0, 
      s: 0 
     }, { 
      l: 0.05, 
      s: 6 
     }, { 
      l: 0.20, 
      s: 18 
     }, { 
      l: 0.38, 
      s: 35 
     }, { 
      l: 0.63, 
      s: 69 
     }, { 
      l: 0.78, 
      s: 89, 
     }, { 
      l: 0.91, 
      s: 100, 
     }, { 
      l: 1, 
      s: 100, 
     }]; 

     // Total number of stops 
     var stopsLength = stops.length; 

     // Loop through stops 
     for (var i = 0; i < stopsLength; i += 1) { 

      // Temp set 
      var currentStop = stops[i]; 

      // Temp set 
      var nextStop = (i + 1 > stopsLength - 1) ? currentStop : stops[i + 1]; 

      // Location is a percentage 
      var satPos; 

      // Temp set 
      var yPos = false; 

      // Convert location to percentage 
      var currentStopLocation = currentStop.l * 100; 

      // Convert location to percentage 
      var nextStopLocation = nextStop.l * 100; 


      // Find which range of hue stops the current color is 
      // Hue is between current and next hue stop 
      if (saturation >= currentStop.s && saturation <= nextStop.s) { 

       // hue is current stop 
       if (saturation === currentStop.s) { 

        // Set as location 
        satPos = currentStopLocation; 

        // hue is next stop 
       } else if (saturation === nextStop.s) { 

        // Set as location 
        satPos = nextStopLocation; 

       // Hue is somewhere between stops 
       } else { 

        // Get percentage location between gradient stops 
        var ratioBetweenSaturation = (saturation - currentStop.s)/(nextStop.s - currentStop.s); 

        // Normalized to fit custom gradient stop locations 
        satPos = ratioBetweenSaturation * (nextStopLocation - currentStopLocation) + currentStopLocation; 
       } 

       console.log('ratioBetweenSaturation', ratioBetweenSaturation); 
       console.log('satPos', satPos); 
       console.log('saturation', saturation, '>=', currentStop.s, saturation, '<=', nextStop.s); 

       // A location was found 
       if (satPos !== false) { 

        // Convert from percentage to pixel position 
        yPos = Math.round(verticalGradientsHeight * (satPos/100)); 

        return yPos; 

       } else { 

        continue; 
       } 
      } 
     } 
    }; 


    // Find brightness in stop range 
    var findBrightnessInStopRange = function (brightness) { 

     // Array of hue stops with HSV, RGB, and HEX info 
     var stops = [{ 
      l: 0, 
      b: 100 
     }, { 
      l: 0.20, 
      b: 88 
     }, { 
      l: 0.28, 
      b: 69 
     }, { 
      l: 0.35, 
      b: 26 
     }, { 
      l: 0.51, 
      b: 9 
     }, { 
      l: 0.83, 
      b: 4, 
     }, { 
      l: 1, 
      b: 0, 
     }]; 

     // Total number of stops 
     var stopsLength = stops.length; 

     // Loop through stops 
     for (var i = 0; i < stopsLength; i += 1) { 

      // Temp set 
      var currentStop = stops[i]; 

      // Temp set 
      var nextStop = (i + 1 > stopsLength - 1) ? currentStop : stops[i + 1]; 

      // Location is a percentage 
      var brightPos; 

      // Temp set 
      var yPos = false; 

      // Convert location to percentage 
      var currentStopLocation = currentStop.l * 100; 

      // Convert location to percentage 
      var nextStopLocation = nextStop.l * 100; 

      console.log('brightness', brightness, '>=', currentStop.b, brightness, '<=', nextStop.b); 


      // Find which range of hue stops the current color is 
      // Hue is between current and next hue stop 
      if (brightness <= currentStop.b && brightness >= nextStop.b) { 

       // hue is current stop 
       if (brightness === currentStop.b) { 

        // Set as location 
        brightPos = currentStopLocation; 

        // hue is next stop 
       } else if (brightness === nextStop.b) { 

        // Set as location 
        brightPos = nextStopLocation; 

       // Hue is somewhere between stops 
       } else { 

        // Get percentage location between gradient stops 
        var ratioBetweenBrightness = (brightness - currentStop.b)/(nextStop.b - currentStop.b); 

        // Normalized to fit custom gradient stop locations 
        brightPos = ratioBetweenBrightness * (nextStopLocation - currentStopLocation) + currentStopLocation; 
       } 

       console.log('ratioBetweenBrightness', ratioBetweenBrightness); 
       console.log('brightPos', brightPos); 
       console.log('brightness', brightness, '>=', currentStop.b, brightness, '<=', nextStop.b); 

       // A location was found 
       if (brightPos !== false) { 

        // Convert from percentage to pixel position 
        yPos = Math.round(verticalGradientsHeight * (brightPos/100)); 

        return yPos; 

       } else { 

        continue; 
       } 
      } 
     } 
    }; 


    // Get coordinates from hue, brightness, saturation 
    var getColorCoordinates = function (hex) { 

     // Convert hex to rgb 
     var rgb = hexToRgb(hex); 
     console.log('rgb', rgb); 
     // Convert rgb to hsb 
     var hsb = rgbToHsb(rgb[0], rgb[1], rgb[2]); 
     console.log('hsb', hsb); 

     // Set x position to position of hue 
     var xPos = findHueInStopRange(hsb.h); 
     var yPos = 0; 


     // if 100, get (containerHeight - verticalGradientHeight) + whatever position is set with bottom gradient 

     // 

     // Saturation and brightness are both maxed 
     if (hsb.s === 100 && hsb.b === 100) { 

      // Set y position at center of container 
      yPos = containerHeight * 0.5; 

     } else { 
       console.log('using nothing', hsb.s, hsb.b); 

      // 
      if (hsb.s < 100) { 

       // Saturation y position (upper quadrant) 
       yPos = findSaturationInStopRange(hsb.s); 
       console.log('using saturation', yPos); 

      } else if (hsb.b < 100) { 

       // Brightness y position (lower quadrant) 
       yPos = findBrightnessInStopRange(hsb.b); 
       console.log('using brightness', yPos); 
      } 
     } 

     return { x: xPos, y: yPos }; 
    } 


    // Get hue location 
    var position = false; 

    // Temp set 
    var hex = '42ad40'; 

    // Draw gradient 
    drawColorSpectrum(); 

    // Find x position 
    position = getColorCoordinates(hex); //91ff26 

    console.log('location', position); 

    // Draw line 
    $('.circle').css({ 
     top: position.y + 'px', 
     left: position.x + 'px', 
     background: '#' + hex 
    }); 
}); 

** CẬP NHẬT **

Tôi thực sự đang cố gắng thực hiện điều này trong HSV chứ không phải HSL. Tôi không có sở thích khác hơn là tôi sử dụng photoshop để tạo ra một gradient mịn.

Ngoài ra, tôi đã thêm một ví dụ mới với những gì tôi đã tạo. Một trong những câu trả lời được bình chọn dưới đây gợi ý làm thế nào để hoàn thành những gì tôi đang cố gắng làm, nhưng cho đến nay tôi đã không thể thực hiện thành công nó.

đang cập nhật sẽ được tại liên kết này, tôi cũng đã cập nhật mã trên: http://codepen.io/shelbywhite/pen/EyqPWY?editors=1000

Trả lời

3

Để có được vị trí bạn cần HSL giá trị

// global 
var RGB = [0,0,0]; // holds the RGB values 0-255 
var LSH = [0,0,0]; // holds the LSH values (note H is normalised to 0-255) 
var rgbToLSH = function(){ 
    var r = RGB[0]/255; 
    var g = RGB[1]/255; 
    var b = RGB[2]/255; 
    var min = Math.min(r,g,b); 
    var max = Math.max(r,g,b); 
    var lum = (min+max)/2; 
    if(lum > 0.5){ 
     var sat = (max-min)/(max+min); 
    }else{ 
     var sat = (max-min)/(2-max-min); 
    } 
    if(r >= b && r >= g){ 
     var hue = (g-b)/(max-min); 
    }else 
    if(b >= b && b >= g){ 
     var hue = 4.0 + (r-g)/(max-min); 
    }else{ 
     var hue = 2.0 + (b-r)/(max-min); 
    } 
    hue *= 60; 
    if(hue < 0) hue += 360; 
    hue = (hue/360); 
    lum = Math.min(1,Math.max(0,lum)); 
    sat = Math.min(1,Math.max(0,sat)); 
    hue = Math.min(1,Math.max(0,hue)); 
    LSH[0] = lum*255; 
    LSH[1] = sat*255; 
    LSH[2] = hue*255; 

} 

Huế sẽ cung cấp cho các vị trí trên trục x và độ bão hòa sẽ cho bạn y từ trên xuống giữa chừng và Độ sáng sẽ cho bạn vị trí trục y từ giữa đến đáy (đi theo ví dụ của bạn);

+0

Cảm ơn sự trợ giúp tại đây. Chuyển đổi từ RGB sang HSL là đủ dễ dàng, nhưng âm mưu màu sắc trên phổ màu thực sự là nơi mà vấn đề nằm. Chủ yếu là bởi vì tôi đang sử dụng một gradient HSL tùy chỉnh, nơi không phải tất cả các màu là 60 độ từ khác. Bạn có nhớ mở rộng thêm với câu trả lời của bạn không? – stwhite

+0

@stWhite Các gradient được sử dụng cho canvas là tuyến tính. Vì vậy, giữ một mảng với Hues và gradient dừng khớp 'st = [{h: 0, s: 0}, {h: 60, s: 0.2}, ...' (tiết kiệm không gian h = hue s = stop) khi bạn nhận được một Hue tìm thấy các mảng mảng màu sắc là giữa 'if (h> = st [i] .h && h <= st [i + 1].h) {'sau đó tìm vị trí' pos = (h-st [i] .h)/(st [i + 1] .h-st [i] .h) 'rồi đến không gian gradient' g = pos * (st [i + 1] .s-st [i] .s) + st [i] .s' bây giờ g được chuẩn hóa cho gradient. Vì vậy, để có được 'xpos = gradientWidth * g'. Tương tự cho lum – Blindman67

+1

@stwhite Đó là hành vi mong đợi. Từ màu đen, xám, trắng không có màu sắc, trả về Huế sẽ cho kết quả sai, nếu Hue là NaN thì màu xám, đen hoặc trắng. Tất cả các giá trị khác đều có màu. – Blindman67

0

Bạn có thể lặp qua toàn bộ ImageData và so sánh cả hai màu.

var findPixelByHex = function(imageData, hex) { 
    var d = imageData.data 
    var w = imageData.width 
    var h = imageData.height 

    for (var y = 0; y < h; ++y) { 
    for (var x = 0; x < w; ++x) { 
     var i = (y * w + x) * 4 
     if (hex === rgbToHex(d[i], d[i + 1], d[i + 2])) { 
     setColorAtPixel(x, y, hex) 
     } 
    } 
    } 
} 

Lưu ý rằng điều này khá chậm và không phải là ý tưởng tốt nhất.

+0

Tôi có thể thấy điều này khá chậm. Tôi tự hỏi nếu chuyển đổi HEX sang RGB thành HSL, lấy màu sắc, và sau đó nhìn thấy khoảng 6 HSL màu nào gần nhất, thì tìm trong phạm vi cách đều nhau từ mỗi bên của phạm vi sẽ nhanh hơn ... ? 'var i = (y * w + x) * 4' – stwhite

+0

Rất tiếc, tôi hiểu dòng chữ đó đang làm gì. – stwhite

Các vấn đề liên quan