diff --git a/app/assets/javascripts/app/lib/app_post/color.coffee b/app/assets/javascripts/app/lib/app_post/color.coffee index b7d9e0e7c..816c9bd83 100644 --- a/app/assets/javascripts/app/lib/app_post/color.coffee +++ b/app/assets/javascripts/app/lib/app_post/color.coffee @@ -1,10 +1,10 @@ # coffeelint: disable=camel_case_classes class App.Color extends Spine.Controller - hsl: undefined + color: undefined + moving: undefined elements: '.js-input': 'input' - '.js-shadow': 'shadow' '.js-swatch': 'swatch' '.js-colorpicker-hue-saturation': 'hueSaturation' '.js-colorpicker-lightness-plane': 'lightnessPlane' @@ -31,110 +31,65 @@ class App.Color extends Spine.Controller @el render: -> - @hsl = @rgbToHsl(@parseColor(@attribute.value)) + @color = new App.ColorObject(@attribute.value) + @html App.view('generic/color') attribute: @attribute - hsl: @hsl + hsl: @color.asHslArray() onInput: -> - @update @input.val() - @output() - - update: (color) -> - @updateSwatch(color) - @hsl = @rgbToHsl(@parseColor(color)) + @color.updateWithString @input.val() @renderPicker() + @updateSwatch @color.asString() - updateSwatch: (color) -> + updateSwatch: (colorString) -> @swatch.css 'background-color', '' - @swatch.css 'background-color', color - - output: -> - hslString = @hslString(@hsl) - @input.val hslString - @updateSwatch hslString - @shadow.val @rgbToHex(@parseColor(hslString)) - - componentToHex: (c) -> - hex = c.toString(16) - if hex.length == 1 then '0' + hex else hex - - rgbToHex: (rgba) -> - '#' + @componentToHex(rgba[0]) + @componentToHex(rgba[1]) + @componentToHex(rgba[2]) - - parseColor: (color) -> - canvas = document.createElement('canvas') - canvas.width = canvas.height = 1 - ctx = canvas.getContext('2d') - ctx.clearRect(0, 0, 1, 1) - ctx.fillStyle = color - ctx.fillRect(0, 0, 1, 1) - ctx.getImageData(0, 0, 1, 1).data - - rgbToHsl: (rgb) -> - return [0, 0, 0] if !rgb - - r = rgb[0] / 255 - g = rgb[1] / 255 - b = rgb[2] / 255 - - max = Math.max(r, g, b) - min = Math.min(r, g, b) - l = (max + min) / 2 - - if (max == min) - h = s = 0 # achromatic - else - d = max - min - s = if l > 0.5 then d / (2 - max - min) else d / (max + min) - - h = switch - when r is max then (g - b) / d + (g < b ? 6 : 0) - when g is max then (b - r) / d + 2 - when b is max then (r - g) / d + 4 - - h /= 6 - - [h, s, l] - - hslString: -> - "hsl(#{Math.round(360 * @hsl[0])},#{Math.round(100 * @hsl[1])}%,#{Math.round(100 * @hsl[2])}%)" + @swatch.css 'background-color', colorString onHueSaturationMousedown: (event) -> - @offset = @hueSaturation.offset() $(document).on 'mousemove.colorpicker', @onHueSaturationMousemove $(document).on 'mouseup.colorpicker', @onMouseup + @onHueSaturationMousemove(event) onHueSaturationMousemove: (event) => - @hsl[0] = Math.max(0, Math.min(1, (event.pageX - @offset.left)/@hueSaturation.width())) - @hsl[1] = Math.max(0, Math.min(1, 1-(event.pageY - @offset.top)/@hueSaturation.height())) + offset = @hueSaturation.offset() + + @color.updateWithHslComponent Math.max(0, Math.min(1, (event.pageX - offset.left)/@hueSaturation.width())), 0 + @color.updateWithHslComponent Math.max(0, Math.min(1, 1-(event.pageY - offset.top)/@hueSaturation.height())), 1 @renderPicker() - @output() + @renderPickerOutput() onLightnessMousedown: (event) -> - @offset = @lightness.offset() $(document).on 'mousemove.colorpicker', @onLightnessMousemove $(document).on 'mouseup.colorpicker', @onMouseup + @onLightnessMousemove(event) onLightnessMousemove: (event) => - @hsl[2] = Math.max(0, Math.min(1, 1-(event.pageY - @offset.top)/@lightness.height())) + offset = @lightness.offset() + + @color.updateWithHslComponent Math.max(0, Math.min(1, 1-(event.pageY - offset.top)/@lightness.height())), 2 @renderPicker() - @output() + @renderPickerOutput() onMouseup: -> $(document).off 'mousemove.colorpicker' $(document).off 'mouseup.colorpicker' + renderPickerOutput: -> + colorString = @color.asString() + @updateSwatch colorString + @input.val colorString + renderPicker: -> - @lightnessPlane.css 'background-color': "hsla(0,0%,#{if @hsl[2] > 0.5 then 100 else 0}%,#{2*Math.abs(@hsl[2]-0.5)})" - @saturationGradient.css 'background-image': "linear-gradient(transparent, hsl(0, 0%, #{@hsl[2]*100}%))" + components = @color.asHslArray() + + @lightnessPlane.css 'background-color': "hsla(0,0%,#{if components[2] > 0.5 then 100 else 0}%,#{2*Math.abs(components[2]-0.5)})" + @saturationGradient.css 'background-image': "linear-gradient(transparent, hsl(0, 0%, #{components[2]*100}%))" @circle.css - left: @hsl[0]*100 +'%' - top: 100 - @hsl[1]*100 +'%' - borderColor: if @hsl[2] > 0.5 then 'black' else 'white' - @huePlane.css 'background-color': "hsl(#{@hsl[0]*360}, 100%, 50%)" - @slider.css top: 100 - @hsl[2]*100 +'%' - - + left: components[0]*100 +'%' + top: 100 - components[1]*100 +'%' + borderColor: if components[2] > 0.5 then 'black' else 'white' + @huePlane.css 'background-color': "hsl(#{components[0]*360}, 100%, 50%)" + @slider.css top: 100 - components[2]*100 +'%' diff --git a/app/assets/javascripts/app/lib/app_post/color_object.coffee b/app/assets/javascripts/app/lib/app_post/color_object.coffee new file mode 100644 index 000000000..574b85d44 --- /dev/null +++ b/app/assets/javascripts/app/lib/app_post/color_object.coffee @@ -0,0 +1,70 @@ +class App.ColorObject + original: undefined + + constructor: (original) -> + @original = original + + asHslArray: -> + if _.isArray(@original) + @original + else + @constructor.anyToHslArray @original + + asString: -> + if _.isArray(@original) + @constructor.hslArrayToHslString(@original) + else + @original + + updateWithString: (newValue) -> + @original = newValue + + updateWithHslComponent: (value, index) -> + if !_.isArray(@original) + @original = @constructor.anyToHslArray @original + + @original[index] = value + + @anyToRgb: (color) -> + canvas = document.createElement('canvas') + canvas.width = canvas.height = 1 + ctx = canvas.getContext('2d') + ctx.clearRect(0, 0, 1, 1) + ctx.fillStyle = color + ctx.fillRect(0, 0, 1, 1) + ctx.getImageData(0, 0, 1, 1).data + + @anyToHslArray: (color) -> + @rgbToHslArray @anyToRgb(color) + + @anyToHslString: (color) -> + @hslArrayToHslString @rgbToHslArray @anyToRgb color + + @rgbToHslArray: (rgb) -> + return [0, 0, 0] if !rgb + + r = rgb[0] / 255 + g = rgb[1] / 255 + b = rgb[2] / 255 + + max = Math.max(r, g, b) + min = Math.min(r, g, b) + l = (max + min) / 2 + + if (max == min) + h = s = 0 # achromatic + else + d = max - min + s = if l > 0.5 then d / (2 - max - min) else d / (max + min) + + h = switch + when r is max then (g - b) / d + (g < b ? 6 : 0) + when g is max then (b - r) / d + 2 + when b is max then (r - g) / d + 4 + + h /= 6 + + [h, s, l] + + @hslArrayToHslString: (hslArray) -> + "hsl(#{Math.round(360 * hslArray[0])},#{Math.round(100 * hslArray[1])}%,#{Math.round(100 * hslArray[2])}%)" diff --git a/app/assets/javascripts/app/views/generic/color.jst.eco b/app/assets/javascripts/app/views/generic/color.jst.eco index d2c01705c..2cca36eb9 100644 --- a/app/assets/javascripts/app/views/generic/color.jst.eco +++ b/app/assets/javascripts/app/views/generic/color.jst.eco @@ -1,6 +1,5 @@