2016-08-26 45 views
8

Mam płótno gradientowe HSV Raindow, które po kliknięciu dodaje element w tym miejscu z jego tłem jako kolorem klikniętego piksela.Obliczyć pozycję piksela X, Y na gradiencie w oparciu o kolor szesnastkowy za pomocą Javascriptu

Chciałbym też, aby działało również w odwrotnej kolejności. Na przykład, jeśli masz kolor heksadecymalny, chciałbym znaleźć ten piksel na płótnie i utworzyć element w tej pozycji.

Moja pierwsza myśl polegała na użyciu systemu macierzy/kwadrantu. Moja następna myśl była taka, że ​​od kiedy używam HSV, mógłbym użyć moich punktów lokalizacji gradientu HSV, aby znaleźć lokalizację. Problem polega na tym, że moje punkty nie są od siebie równe, co sprawia, że ​​jest trudniej. Ponadto mam biały gradient i czarny gradient pokrywający główny gradient kolorów i muszę to uwzględnić.

Moje pytanie brzmi: jak znaleźć położenie piksela koloru lub przynajmniej jego najbliższe dopasowanie za pomocą kodu szesnastkowego?

Oto mój kod do tej pory: 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 
    }); 
}); 

** AKTUALIZACJA **

Próbuję to zrobić w HSV, a nie HSL. Nie mam innych preferencji poza tym, że użyłem Photoshopa do wygenerowania gładkiego gradientu.

Dodałem też nowy przykład z tym, co stworzyłem. Jedna z wyżej wymienionych odpowiedzi poniżej podpowiada, jak osiągnąć to, co próbuję zrobić, ale jak dotąd nie udało mi się tego zrobić.

Zaktualizowany kod będzie pod tym linkiem, ja również uaktualniony kod powyżej: http://codepen.io/shelbywhite/pen/EyqPWY?editors=1000

Odpowiedz

3

aby uzyskać lokalizację, potrzebują Państwo HSL wartości

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

} 

Hue dadzą pozycję na Oś x i Nasycenie da Ci y z góry na środek, a Jasność da ci pozycję osi y od połowy do dołu (idąc twoim przykładem);

+0

Dzięki za pomoc tutaj. Konwersja z RGB na HSL jest dość łatwa, ale wykreślenie koloru na spektrum kolorów jest naprawdę tam, gdzie leży problem. Głównie dlatego, że używam niestandardowego gradientu HSL, w którym nie każdy kolor ma 60 stopni od drugiego. Czy mógłbyś bardziej rozwinąć swoją odpowiedź? – stwhite

+0

@stWhite Gradienty używane na płótnie są liniowe. Więc utrzymuj tablicę z Hues i dopasowanym stopniem gradientu 'st = [{h: 0, s: 0}, {h: 60, s: 0.2}, ...' (oszczędność miejsca h = hue s = stop) gdy dostaniesz odcień, odszukaj elementy tablicy, których odcień znajduje się pomiędzy 'if (h> = st [i] .h && h <= st [i + 1].h) {'następnie znajdź pozycję' pos = (h-st [i] .h)/(st [i + 1] .h-st [i] .h) 'następnie do przestrzeni gradientowej' g = pos * (st [i + 1] .s-st [i] .s) + st [i] .s' teraz g jest znormalizowany dla gradientu. Tak więc, aby uzyskać 'xpos = gradientWidth * g'. To samo dla lum – Blindman67

+1

@stwhite To jest oczekiwane zachowanie. Od czerni, szarości, do bieli nie ma odcienia, powrót odcienia dawałby zły wynik, jeśli odcień jest naN, kolor jest szary, czarny lub biały. Wszystkie inne wartości mają kolor. – Blindman67

0

Można wykonać iterację całego zestawu ImageData i porównać oba kolory.

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) 
     } 
    } 
    } 
} 

Należy pamiętać, że jest to dość powolny i nie najlepszy pomysł.

+0

Widziałem, że jest to dość powolne. Zastanawiam się, czy konwertując HEX na RGB na HSL, chwytając odcień, a następnie widząc, który z 6 zakresów odcieni HSL jest najbliżej, to spojrzenie w równej odległości od każdej strony zakresu byłoby szybsze ... także co robi ta linia robi ? 'var i = (y * w + x) * 4' – stwhite

+0

Ups, nieważne, rozumiem, co robi ta linia. – stwhite