Simpler gradient output by discarding from() and to()

This commit is contained in:
Chris Eppstein 2010-02-21 22:44:38 -08:00
parent 34478112ba
commit ad4a486ea1
4 changed files with 212 additions and 97 deletions

View File

@ -1,44 +1,58 @@
=gradient(!type, !coords, !color_start, !color_end, !color_stop = false) // This yields a linear gradient spanning from top to bottom
!gradient= "#{!coords}, from(#{!color_start}), to(#{!color_end})" //
@if !color_stop // +linear-gradient(color_stops(white, black))
!gradient= !gradient + ", " + !color_stop //
background: -webkit-gradient(#{!type}, #{!gradient}) // This yields a linear gradient spanning from bottom to top
background: -moz-#{!type}-gradient(#{!gradient}) //
// +linear-gradient(color_stops(white, black), "bottom")
//* //
// This will yield a radial gradient with an apparent specular highlight // This yields a linear gradient spanning from left to right
+radial-gradient("45 45, 10, 52 50, 30", Cyan, DodgerBlue) //
// +linear-gradient(color_stops(white, black), "left")
=radial-gradient(!coords, !color1, !color2, !color_stop = false) //
+gradient("radial", !coords, !color1, !color2, !color_stop) // This yields a linear gradient starting at white passing
// thru blue at 33% down and then to black
//* //
// This yields a linear gradient spanning from top to bottom // +linear-gradient(color_stops(white, blue 33%, black))
+linear-gradient(color_stops(white, black)) //
// This yields a linear gradient starting at white passing
// This yields a linear gradient spanning from bottom to top // thru blue at 33% down and then to black at 67% until the end
+linear-gradient(color_stops(white, black), "bottom") //
// +linear-gradient(color_stops(white, blue 33%, black 67%))
// This yields a linear gradient spanning from left to right //
+linear-gradient(color_stops(white, black), "left") // Browsers Supported:
//
// This yields a linear gradient starting at white passing // - Chrome
// thru blue at 33% down and then to black // - Safari
+linear-gradient(color_stops(white, blue 33%, black)) // - Firefox 3.6
// This yields a linear gradient starting at white passing
// thru blue at 33% down and then to black at 67% until the end
+linear-gradient(color_stops(white, blue 33%, black 67%))
=linear-gradient(!color_stops, !start = "top") =linear-gradient(!color_stops, !start = "top")
// Firefox's gradient api is nice. // Firefox's gradient api is nice.
// Webkit's gradient api sucks -- hence these backflips: // Webkit's gradient api sucks -- hence these backflips:
!end = grad_opposite_position(!start) !end = grad_opposite_position(!start)
!start_color = grad_start_color(!color_stops) background-image: -webkit-gradient(linear, #{grad_point(!start)}, #{grad_point(!end)}, #{grad_color_stops(!color_stops)})
!end_color = grad_end_color(!color_stops)
!webkit_stops = grad_color_stops(!color_stops)
!gradient= "#{grad_point(!start)}, #{grad_point(!end)}, from(#{!start_color}), to(#{!end_color})"
@if !webkit_stops
!gradient= !gradient + ", " + !webkit_stops
background-image: -webkit-gradient(linear, #{!gradient})
background-image: -moz-linear-gradient(#{!start}, #{!color_stops}) background-image: -moz-linear-gradient(#{!start}, #{!color_stops})
// Due to limitation's of webkit, the radial gradient mixin works best if you use
// pixel-based color stops.
//
// Examples:
//
// // Defaults to a centered, 100px radius gradient
// +radial-gradient(color_stops(#c00, #00c))
// // 100px radius gradient in the top left corner
// +radial-gradient(color_stops(#c00, #00c), "top left")
// // Three colors, ending at 50px and passing thru #fff at 25px
// +radial-gradient(color_stops(#c00, #fff, #00c 50px))
//
// Browsers Supported:
//
// - Chrome
// - Safari
// - Firefox 3.6
=radial-gradient(!color_stops, !center_position = "center center")
!end_pos = grad_end_position(!color_stops, true)
background-image: -webkit-gradient(radial, #{grad_point(!center_position)}, 0, #{grad_point(!center_position)}, #{!end_pos}, #{grad_color_stops(!color_stops)})
background-image: -moz-radial-gradient(#{!center_position}, circle, #{!color_stops})

View File

@ -25,8 +25,11 @@ module Compass::SassExtensions::Functions::GradientSupport
s = "#{color}" s = "#{color}"
if stop if stop
s << " " s << " "
s << stop.times(Sass::Script::Number.new(100)).to_s if stop.unitless?
s << "%" s << stop.times(Sass::Script::Number.new(100, ["%"])).to_s
else
s << stop.to_s
end
end end
s s
end end
@ -41,6 +44,7 @@ module Compass::SassExtensions::Functions::GradientSupport
when "bottom" then "top" when "bottom" then "top"
when "left" then "right" when "left" then "right"
when "right" then "left" when "right" then "left"
when "center" then "center"
else else
raise Sass::SyntaxError, "Cannot determine the opposite of #{pos}" raise Sass::SyntaxError, "Cannot determine the opposite of #{pos}"
end end
@ -50,49 +54,53 @@ module Compass::SassExtensions::Functions::GradientSupport
# returns color-stop() calls for use in webkit. # returns color-stop() calls for use in webkit.
def grad_color_stops(color_list) def grad_color_stops(color_list)
positions = color_list.values.map{|c| [c.stop && c.stop.value, c.color]} normalize_stops!(color_list)
# fill in the blank positions max = color_list.values.last.stop
positions[0][0] = 0 unless positions.first.first color_stops = color_list.values.map do |pos|
positions[positions.size - 1][0] = 1 unless positions.last.first # have to convert absolute units to percentages for use in color stop functions.
for i in 0...positions.size stop = pos.stop
if positions[i].first.nil? stop = stop.div(max).times(Sass::Script::Number.new(100,["%"])) if stop.numerator_units == max.numerator_units
num = 2.0 "color-stop(#{stop}, #{pos.color})"
for j in (i+1)...positions.size end
if positions[j].first Sass::Script::String.new(color_stops.join(", "))
positions[i][0] = positions[i-1].first + (positions[j].first - positions[i-1].first) / num end
break
else # returns the end position of the gradient from the color stop
num += 1 def grad_end_position(color_list, radial = Sass::Script::Bool.new(false))
end default = Sass::Script::Number.new(100)
grad_position(color_list, Sass::Script::Number.new(color_list.values.size), default, radial)
end
def grad_position(color_list, index, default, radial = Sass::Script::Bool.new(false))
stop = color_list.values[index.value - 1].stop
if stop && radial.to_bool
orig_stop = stop
if stop.unitless?
if stop.value <= 1
# A unitless number is assumed to be a percentage when it's between 0 and 1
stop = stop.times(Sass::Script::Number.new(100, ["%"]))
else
# Otherwise, a unitless number is assumed to be in pixels
stop = stop.times(Sass::Script::Number.new(1, ["px"]))
end end
end end
end if stop.numerator_units == ["%"] && color_list.values.last.stop && color_list.values.last.stop.numerator_units == ["px"]
positions = positions[1..-1] if positions.first.first == 0.0 stop = stop.times(color_list.values.last.stop).div(Sass::Script::Number.new(100, ["%"]))
positions = positions[0..-2] if positions.last.first == 1.0 end
if positions.empty? Compass::Logger.new.record(:warning, "Webkit only supports pixels for the start and end stops for radial gradients. Got: #{orig_stop}") if stop.numerator_units != ["px"]
# We return false here since sass has no nil. stop.div(Sass::Script::Number.new(1, stop.numerator_units, stop.denominator_units))
Sass::Script::Bool.new(false) elsif stop
stop
else else
color_stops = positions.map {|pos| "color-stop(#{Sass::Script::Number.new((pos.first*10000).round/100.0).to_s}%, #{pos.last})"} default
Sass::Script::String.new(color_stops.join(", "))
end end
end end
# the first color from a list of color stops
def grad_start_color(color_list)
color_list.values.first.color
end
# the last color from a list of color stops
def grad_end_color(color_list)
color_list.values.last.color
end
# the given a position, return a point in percents # the given a position, return a point in percents
def grad_point(position) def grad_point(position)
position = position.value position = position.value
position = if position[" "] position = if position[" "]
if position =~ /(top|bottom) (left|right)/ if position =~ /(top|bottom|center) (left|right|center)/
"#{$2} #{$1}" "#{$2} #{$1}"
else else
position position
@ -103,9 +111,16 @@ module Compass::SassExtensions::Functions::GradientSupport
"left #{position}" "left #{position}"
when /left|right/ when /left|right/
"#{position} top" "#{position} top"
else
position
end end
end end
Sass::Script::String.new position.gsub(/top/, "0%").gsub(/bottom/, "100%").gsub(/left/,"0%").gsub(/right/,"100%") Sass::Script::String.new(position.
gsub(/top/, "0%").
gsub(/bottom/, "100%").
gsub(/left/,"0%").
gsub(/right/,"100%").
gsub(/center/, "50%"))
end end
def color_stops(*args) def color_stops(*args)
@ -125,7 +140,11 @@ module Compass::SassExtensions::Functions::GradientSupport
end end
stop = Sass::Script::Number.new(number) stop = Sass::Script::Number.new(number)
elsif !stop.nil? elsif !stop.nil?
raise Sass::SyntaxError, "A color stop location must be between 0 and 1 or 0% and 100%. Got: #{stop}" number = Sass::Script::Parser.parse(stop, 0, 0)
unless number.is_a?(Sass::Script::Number)
raise Sass::SyntaxError, "A color stop location must be a number. Got: #{stop}"
end
stop = number
end end
ColorStop.new(color, stop) ColorStop.new(color, stop)
else else
@ -133,5 +152,35 @@ module Compass::SassExtensions::Functions::GradientSupport
end end
end) end)
end end
private
def normalize_stops!(color_list)
positions = color_list.values
# fill in the start and end positions, if unspecified
positions.first.stop = Sass::Script::Number.new(0) unless positions.first.stop
positions.last.stop = Sass::Script::Number.new(100, ["%"]) unless positions.last.stop
# fill in empty values
for i in 0...positions.size
if positions[i].stop.nil?
num = 2.0
for j in (i+1)...positions.size
if positions[j].stop
positions[i].stop = positions[i-1].stop.plus((positions[j].stop.minus(positions[i-1].stop)).div(Sass::Script::Number.new(num)))
break
else
num += 1
end
end
end
end
# normalize unitless numbers
positions.each do |pos|
if pos.stop.unitless? && pos.stop.value <= 1
pos.stop = pos.stop.times(Sass::Script::Number.new(100, ["%"]))
elsif pos.stop.unitless?
pos.stop = pos.stop.times(Sass::Script::Number.new(1, ["px"]))
end
end
nil
end
end end
end end

View File

@ -1,43 +1,71 @@
.linear-1 { .linear-1 {
background-image: -webkit-gradient(linear, 0% 0%, 100% 0%, from(#dddddd), to(#aaaaaa)); background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(0%, #dddddd), color-stop(100%, #aaaaaa));
background-image: -moz-linear-gradient(top, #dddddd, #aaaaaa); } background-image: -moz-linear-gradient(top, #dddddd 0%, #aaaaaa 100%); }
.linear-2 { .linear-2 {
background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#dddddd), to(#aaaaaa)); background-image: -webkit-gradient(linear, 0% 0%, 100% 0%, color-stop(0%, #dddddd), color-stop(100%, #aaaaaa));
background-image: -moz-linear-gradient(left, #dddddd, #aaaaaa); } background-image: -moz-linear-gradient(left, #dddddd 0%, #aaaaaa 100%); }
.linear-3 { .linear-3 {
background-image: -webkit-gradient(linear, 0% 0%, 100% 100%, from(#dddddd), to(#aaaaaa)); background-image: -webkit-gradient(linear, 0% 0%, 100% 100%, color-stop(0%, #dddddd), color-stop(100%, #aaaaaa));
background-image: -moz-linear-gradient(top left, #dddddd, #aaaaaa); } background-image: -moz-linear-gradient(top left, #dddddd 0%, #aaaaaa 100%); }
.linear-4 { .linear-4 {
background-image: -webkit-gradient(linear, 0% 100%, 100% 0%, from(#dddddd), to(#aaaaaa)); background-image: -webkit-gradient(linear, 100% 0%, 0% 100%, color-stop(0%, #dddddd), color-stop(100%, #aaaaaa));
background-image: -moz-linear-gradient(top right, #dddddd, #aaaaaa); } background-image: -moz-linear-gradient(top right, #dddddd 0%, #aaaaaa 100%); }
.linear-5 { .linear-5 {
background-image: -webkit-gradient(linear, 0% 0%, 100% 0%, from(#dddddd), to(#aaaaaa), color-stop(50%, #cccccc)); background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(0%, #dddddd), color-stop(50%, #cccccc), color-stop(100%, #aaaaaa));
background-image: -moz-linear-gradient(top, #dddddd, #cccccc, #aaaaaa); } background-image: -moz-linear-gradient(top, #dddddd 0%, #cccccc 50%, #aaaaaa 100%); }
.linear-6 { .linear-6 {
background-image: -webkit-gradient(linear, 0% 0%, 100% 0%, from(#dddddd), to(#aaaaaa), color-stop(20%, #cccccc)); background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(0%, #dddddd), color-stop(20%, #cccccc), color-stop(100%, #aaaaaa));
background-image: -moz-linear-gradient(top, #dddddd, #cccccc 20%, #aaaaaa); } background-image: -moz-linear-gradient(top, #dddddd 0%, #cccccc 20%, #aaaaaa 100%); }
.linear-7 { .linear-7 {
background-image: -webkit-gradient(linear, 0% 0%, 100% 0%, from(#dddddd), to(#aaaaaa), color-stop(20%, #cccccc), color-stop(60%, #eeeeee)); background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(0%, #dddddd), color-stop(20%, #cccccc), color-stop(50.1%, #eeeeee), color-stop(100%, #aaaaaa));
background-image: -moz-linear-gradient(top, #dddddd, #cccccc 20%, #eeeeee, #aaaaaa); } background-image: -moz-linear-gradient(top, #dddddd 0%, #cccccc 20%, #eeeeee 50.1%, #aaaaaa 100%); }
.linear-8 { .linear-8 {
background-image: -webkit-gradient(linear, 0% 0%, 100% 0%, from(#dddddd), to(#aaaaaa), color-stop(80%, #dddddd)); background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(80%, #dddddd), color-stop(100%, #aaaaaa));
background-image: -moz-linear-gradient(top, #dddddd 80%, #aaaaaa); } background-image: -moz-linear-gradient(top, #dddddd 80%, #aaaaaa 100%); }
.linear-9 { .linear-9 {
background-image: -webkit-gradient(linear, 0% 0%, 100% 0%, from(#dddddd), to(#aaaaaa), color-stop(20%, #aaaaaa)); background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(0%, #dddddd), color-stop(100%, #aaaaaa));
background-image: -moz-linear-gradient(top, #dddddd, #aaaaaa 20%); } background-image: -moz-linear-gradient(top, #dddddd 0%, #aaaaaa 20%); }
.linear-10 { .linear-10 {
background-image: -webkit-gradient(linear, 0% 0%, 100% 0%, from(#dddddd), to(#aaaaaa), color-stop(40%, #dddddd), color-stop(50%, #aaaaaa)); background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(80%, #dddddd), color-stop(100%, #aaaaaa));
background-image: -moz-linear-gradient(top, #dddddd 40%, #aaaaaa 50%); } background-image: -moz-linear-gradient(top, #dddddd 40%, #aaaaaa 50%); }
.linear-11 { .linear-11 {
background-image: -webkit-gradient(linear, 0% 0%, 100% 0%, from(#dddddd), to(#aaaaaa), color-stop(40%, #dddddd), color-stop(45%, black), color-stop(50%, #aaaaaa)); background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(80%, #dddddd), color-stop(90%, black), color-stop(100%, #aaaaaa));
background-image: -moz-linear-gradient(top, #dddddd 40%, black, #aaaaaa 50%); } background-image: -moz-linear-gradient(top, #dddddd 40%, black 45%, #aaaaaa 50%); }
.radial-1 {
background-image: -webkit-gradient(radial, 50% 50%, 0, 50% 50%, 100, color-stop(0%, #dddddd), color-stop(100%, #aaaaaa));
background-image: -moz-radial-gradient(center center, circle, #dddddd 0%, #aaaaaa 100%); }
.radial-2 {
background-image: -webkit-gradient(radial, 50% 50%, 0, 50% 50%, 100, color-stop(0%, #dddddd), color-stop(100%, #aaaaaa));
background-image: -moz-radial-gradient(center center, circle, #dddddd 0%, #aaaaaa 100%); }
.radial-3 {
background-image: -webkit-gradient(radial, 50% 0%, 0, 50% 0%, 100, color-stop(0%, #dddddd), color-stop(100%, #aaaaaa));
background-image: -moz-radial-gradient(top center, circle, #dddddd 0%, #aaaaaa 100%); }
.radial-4 {
background-image: -webkit-gradient(radial, 50% 50%, 0, 50% 50%, 100, color-stop(0%, #dddddd), color-stop(100%, #aaaaaa));
background-image: -moz-radial-gradient(center center, circle, #dddddd 0%, #aaaaaa 100%); }
.radial-5 {
background-image: -webkit-gradient(radial, 50% 0%, 0, 50% 0%, 100, color-stop(0%, #dddddd), color-stop(100%, #aaaaaa));
background-image: -moz-radial-gradient(top center, circle, #dddddd 0%, #aaaaaa 100%); }
.radial-6 {
background-image: -webkit-gradient(radial, 50% 50%, 0, 50% 50%, 50, color-stop(40%, #dddddd), color-stop(100%, #aaaaaa));
background-image: -moz-radial-gradient(center center, circle, #dddddd 20px, #aaaaaa 50px); }
.radial-7 {
background-image: -webkit-gradient(radial, 50% 50%, 0, 50% 50%, 50, color-stop(20%, #dddddd), color-stop(100%, #aaaaaa));
background-image: -moz-radial-gradient(center center, circle, #dddddd 20%, #aaaaaa 50px); }

View File

@ -21,4 +21,28 @@
.linear-10 .linear-10
+linear-gradient(color_stops(#ddd 40%, #aaa 50%)) +linear-gradient(color_stops(#ddd 40%, #aaa 50%))
.linear-11 .linear-11
+linear-gradient(color_stops(#ddd 40%, #000, #aaa 50%)) +linear-gradient(color_stops(#ddd 40%, #000, #aaa 50%))
.radial-1
// A default radial gradient:
// A centered gradient having the shape of the container (aka ellipse)
+radial-gradient(color_stops(#ddd, #aaa))
.radial-2
// A centered gradient having the shape of the container (aka ellipse)
+radial-gradient(color_stops(#ddd, #aaa))
.radial-3
// A centered gradient at the top having the shape of the container (aka ellipse)
+radial-gradient(color_stops(#ddd, #aaa), "top center")
.radial-4
// A centered gradient having a circular shape
+radial-gradient(color_stops(#ddd, #aaa))
.radial-5
// A centered gradient at the top having a circular shape
+radial-gradient(color_stops(#ddd, #aaa), "top center")
.radial-6
// A centered circular gradient with color stops
// The color stops must be absolute units
+radial-gradient(color_stops(#ddd 20px, #aaa 50px))
.radial-7
// A centered elliptical gradient with color stops
// The color stops must be relative units
+radial-gradient(color_stops(#ddd 20%, #aaa 50px))