Skip to content

Commit

Permalink
Allow degrees for linear and radial gradients. Currently no support f…
Browse files Browse the repository at this point in the history
…or SVG gradients using degrees. Partial fix for #303
  • Loading branch information
chriseppstein committed Apr 10, 2011
1 parent 73a01b3 commit ad0e172
Show file tree
Hide file tree
Showing 6 changed files with 81 additions and 55 deletions.
2 changes: 1 addition & 1 deletion doc-src/Gemfile
Expand Up @@ -16,6 +16,6 @@ gem 'compass', :path => ".."
gem 'compass-susy-plugin', ">=0.7.0.pre8"
gem 'css-slideshow', "0.2.0"
gem 'json'
gem 'css_parser'
gem 'css_parser', "1.0.1"
gem 'ruby-prof'

4 changes: 2 additions & 2 deletions doc-src/Gemfile.lock
Expand Up @@ -23,7 +23,7 @@ GEM
cri (1.0.1)
css-slideshow (0.2.0)
compass (>= 0.10.0.rc3)
css_parser (1.1.9)
css_parser (1.0.1)
fssm (0.1.2)
haml (3.1.0.alpha.147)
i18n (0.4.2)
Expand Down Expand Up @@ -51,7 +51,7 @@ DEPENDENCIES
compass!
compass-susy-plugin (>= 0.7.0.pre8)
css-slideshow (= 0.2.0)
css_parser
css_parser (= 1.0.1)
fssm (= 0.1.2)
haml (>= 3.1.0.alpha.36)
json
Expand Down
7 changes: 7 additions & 0 deletions lib/compass/sass_extensions/functions/constants.rb
@@ -1,5 +1,12 @@
module Compass::SassExtensions::Functions::Constants
if defined?(Sass::Script::List)
POSITIONS = /top|bottom|left|right|center/
def is_position(position)
Sass::Script::Bool.new(position.is_a?(Sass::Script::String) && !!(position.value =~ POSITIONS))
end
def is_position_list(position_list)
Sass::Script::Bool.new(position_list.is_a?(Sass::Script::List) && position_list.value.all?{|p| is_position(p).to_bool})
end
# returns the opposite position of a side or corner.
def opposite_position(position)
position = unless position.is_a?(Sass::Script::List)
Expand Down
99 changes: 61 additions & 38 deletions lib/compass/sass_extensions/functions/gradient_support.rb
Expand Up @@ -34,15 +34,15 @@ def to_s(options = self.options)
end

class RadialGradient < Sass::Script::Literal
attr_accessor :position_and_angle, :shape_and_size, :color_stops
attr_accessor :position_or_angle, :shape_and_size, :color_stops
def children
[color_stops, position_and_angle, shape_and_size].compact
[color_stops, position_or_angle, shape_and_size].compact
end
def initialize(position_and_angle, shape_and_size, color_stops)
def initialize(position_or_angle, shape_and_size, color_stops)
unless color_stops.value.size >= 2
raise Sass::SyntaxError, "At least two color stops are required for a radial-gradient"
end
self.position_and_angle = position_and_angle
self.position_or_angle = position_or_angle
self.shape_and_size = shape_and_size
self.color_stops = color_stops
end
Expand All @@ -51,22 +51,31 @@ def inspect
end
def to_s(options = self.options)
s = "radial-gradient("
s << position_and_angle.to_s(options) << ", " if position_and_angle
s << position_or_angle.to_s(options) << ", " if position_or_angle
s << shape_and_size.to_s(options) << ", " if shape_and_size
s << color_stops.to_s(options)
s << ")"
end
def supports?(aspect)
GRADIENT_ASPECTS.include?(aspect)
if aspect == "svg"
if position_or_angle.nil? || is_position(position_or_angle).to_bool || is_position_list(position_or_angle).to_bool
true
else
Compass::Util.compass_warn("Warning: Angle-based gradients are not yet supported in SVG. Found: #{position_or_angle}")
false
end
else
GRADIENT_ASPECTS.include?(aspect)
end
end
def has_aspect?
true
end
def to_webkit(options = self.options)
args = [
grad_point(position_and_angle || _center_position),
grad_point(position_or_angle || _center_position),
Sass::Script::String.new("0"),
grad_point(position_and_angle || _center_position),
grad_point(position_or_angle || _center_position),
grad_end_position(color_stops, Sass::Script::Bool.new(true)),
grad_color_stops(color_stops)
]
Expand All @@ -82,7 +91,7 @@ def to_o(options = self.options)
end
def to_svg(options = self.options)
# XXX Add shape support if possible
radial_svg_gradient(color_stops, position_and_angle || _center_position)
radial_svg_gradient(color_stops, position_or_angle || _center_position)
end
def to_pie(options = self.options)
Compass::Logger.new.record(:warning, "PIE does not support radial-gradient.")
Expand All @@ -94,36 +103,45 @@ def to_css2(options = self.options)
end

class LinearGradient < Sass::Script::Literal
attr_accessor :color_stops, :position_and_angle
attr_accessor :color_stops, :position_or_angle
def children
[color_stops, position_and_angle].compact
[color_stops, position_or_angle].compact
end
def initialize(position_and_angle, color_stops)
def initialize(position_or_angle, color_stops)
unless color_stops.value.size >= 2
raise Sass::SyntaxError, "At least two color stops are required for a linear-gradient"
end
self.position_and_angle = position_and_angle
self.position_or_angle = position_or_angle
self.color_stops = color_stops
end
def inspect
to_s
end
def to_s(options = self.options)
s = "linear-gradient("
s << position_and_angle.to_s(options) << ", " if position_and_angle
s << position_or_angle.to_s(options) << ", " if position_or_angle
s << color_stops.to_s(options)
s << ")"
end
def supports?(aspect)
GRADIENT_ASPECTS.include?(aspect)
if aspect == "svg"
if position_or_angle.nil? || is_position(position_or_angle).to_bool || is_position_list(position_or_angle).to_bool
true
else
Compass::Util.compass_warn("Warning: Angle-based gradients are not yet supported in SVG. Found: #{position_or_angle}")
false
end
else
GRADIENT_ASPECTS.include?(aspect)
end
end
def has_aspect?
true
end
def to_webkit(options = self.options)
args = []
args << grad_point(position_and_angle || Sass::Script::String.new("top"))
args << grad_point(opposite_position(position_and_angle || Sass::Script::String.new("top")))
args << grad_point(position_or_angle || Sass::Script::String.new("top"))
args << grad_point(opposite_position(position_or_angle || Sass::Script::String.new("top")))
args << grad_color_stops(color_stops)
args.each{|a| a.options = options}
Sass::Script::String.new("-webkit-gradient(linear, #{args.join(', ')})")
Expand All @@ -135,7 +153,7 @@ def to_o(options = self.options)
Sass::Script::String.new("-o-#{to_s(options)}")
end
def to_svg(options = self.options)
linear_svg_gradient(color_stops, position_and_angle || Sass::Script::String.new("top"))
linear_svg_gradient(color_stops, position_or_angle || Sass::Script::String.new("top"))
end
def to_pie(options = self.options)
# PIE just uses the standard rep, but the property is prefixed so
Expand All @@ -150,13 +168,18 @@ def to_css2(options = self.options)
module Functions

# given a position list, return a corresponding position in percents
# otherwise, returns the original argument
def grad_point(position)
original_value = position
position = unless position.is_a?(Sass::Script::List)
Sass::Script::List.new([position], :space)
else
Sass::Script::List.new(position.value.dup, position.separator)
end
position.value.reject!{|p| p.is_a?(Sass::Script::Number) && p.numerator_units.include?("deg")}
# Handle unknown arguments by passing them along untouched.
unless position.value.all?{|p| is_position(p).to_bool }
return original_value
end
if (position.value.first.value =~ /top|bottom/) or (position.value.last.value =~ /left|right/)
# browsers are pretty forgiving of reversed positions so we are too.
position.value.reverse!
Expand Down Expand Up @@ -198,7 +221,7 @@ def color_stops(*args)
end, :comma)
end

def radial_gradient(position_and_angle, shape_and_size, *color_stops)
def radial_gradient(position_or_angle, shape_and_size, *color_stops)
# Have to deal with variable length/meaning arguments.
if color_stop?(shape_and_size)
color_stops.unshift(shape_and_size)
Expand All @@ -209,38 +232,38 @@ def radial_gradient(position_and_angle, shape_and_size, *color_stops)
shape_and_size = nil
end
shape_and_size = nil if shape_and_size && !shape_and_size.to_bool # nil out explictly passed falses
# ditto for position_and_angle
if color_stop?(position_and_angle)
color_stops.unshift(position_and_angle)
position_and_angle = nil
elsif list_of_color_stops?(position_and_angle)
color_stops = position_and_angle.value + color_stops
position_and_angle = nil
# ditto for position_or_angle
if color_stop?(position_or_angle)
color_stops.unshift(position_or_angle)
position_or_angle = nil
elsif list_of_color_stops?(position_or_angle)
color_stops = position_or_angle.value + color_stops
position_or_angle = nil
end
position_and_angle = nil if position_and_angle && !position_and_angle.to_bool
position_or_angle = nil if position_or_angle && !position_or_angle.to_bool

# Support legacy use of the color-stops() function
if color_stops.size == 1 && list_of_color_stops?(color_stops.first)
color_stops = color_stops.first.value
end
RadialGradient.new(position_and_angle, shape_and_size, send(:color_stops, *color_stops))
RadialGradient.new(position_or_angle, shape_and_size, send(:color_stops, *color_stops))
end

def linear_gradient(position_and_angle, *color_stops)
if color_stop?(position_and_angle)
color_stops.unshift(position_and_angle)
position_and_angle = nil
elsif list_of_color_stops?(position_and_angle)
color_stops = position_and_angle.value + color_stops
position_and_angle = nil
def linear_gradient(position_or_angle, *color_stops)
if color_stop?(position_or_angle)
color_stops.unshift(position_or_angle)
position_or_angle = nil
elsif list_of_color_stops?(position_or_angle)
color_stops = position_or_angle.value + color_stops
position_or_angle = nil
end
position_and_angle = nil if position_and_angle && !position_and_angle.to_bool
position_or_angle = nil if position_or_angle && !position_or_angle.to_bool

# Support legacy use of the color-stops() function
if color_stops.size == 1 && (stops = list_of_color_stops?(color_stops.first))
color_stops = stops
end
LinearGradient.new(position_and_angle, send(:color_stops, *color_stops))
LinearGradient.new(position_or_angle, send(:color_stops, *color_stops))
end

# returns color-stop() calls for use in webkit.
Expand Down
20 changes: 8 additions & 12 deletions test/fixtures/stylesheets/compass/css/gradients.css
Expand Up @@ -43,20 +43,16 @@
background-image: radial-gradient(center center, #dddddd, #aaaaaa 100px); }

.bg-linear-gradient-with-angle {
background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiB4MT0iMCUiIHkxPSIwJSIgeDI9IjEwMCUiIHkyPSIxMDAlIj48c3RvcCBvZmZzZXQ9IjAlIiBzdG9wLWNvbG9yPSIjZGRkZGRkIi8+PHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjYWFhYWFhIi8+PC9saW5lYXJHcmFkaWVudD48L2RlZnM+PHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0idXJsKCNncmFkKSIgLz48L3N2Zz4g');
background-size: 100%;
background-image: -webkit-gradient(linear, 0% 0%, 100% 100%, color-stop(0%, #dddddd), color-stop(100%, #aaaaaa));
background-image: -moz-linear-gradient(top left -45deg, #dddddd, #aaaaaa);
background-image: -o-linear-gradient(top left -45deg, #dddddd, #aaaaaa);
background-image: linear-gradient(top left -45deg, #dddddd, #aaaaaa); }
background-image: -webkit-gradient(linear, -45deg, -45deg, color-stop(0%, #dddddd), color-stop(100%, #aaaaaa));
background-image: -moz-linear-gradient(-45deg, #dddddd, #aaaaaa);
background-image: -o-linear-gradient(-45deg, #dddddd, #aaaaaa);
background-image: linear-gradient(-45deg, #dddddd, #aaaaaa); }

.bg-radial-gradient-with-angle-and-shape {
background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PHJhZGlhbEdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgY3g9IjUwJSIgY3k9IjUwJSIgcj0iMTAwIj48c3RvcCBvZmZzZXQ9IjAlIiBzdG9wLWNvbG9yPSIjZGRkZGRkIi8+PHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjYWFhYWFhIi8+PC9yYWRpYWxHcmFkaWVudD48L2RlZnM+PHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0idXJsKCNncmFkKSIgLz48L3N2Zz4g');
background-size: 100%;
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 45deg, ellipse cover, #dddddd, #aaaaaa 100px);
background-image: -o-radial-gradient(center center 45deg, ellipse cover, #dddddd, #aaaaaa 100px);
background-image: radial-gradient(center center 45deg, ellipse cover, #dddddd, #aaaaaa 100px); }
background-image: -webkit-gradient(radial, 45deg, 0, 45deg, 100, color-stop(0%, #dddddd), color-stop(100%, #aaaaaa));
background-image: -moz-radial-gradient(45deg, ellipse cover, #dddddd, #aaaaaa 100px);
background-image: -o-radial-gradient(45deg, ellipse cover, #dddddd, #aaaaaa 100px);
background-image: radial-gradient(45deg, ellipse cover, #dddddd, #aaaaaa 100px); }

.bg-all-gradient-types {
background-image: url('/images/4x6.png?busted=true'), url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiB4MT0iMCUiIHkxPSIwJSIgeDI9IjEwMCUiIHkyPSIxMDAlIj48c3RvcCBvZmZzZXQ9IjAlIiBzdG9wLWNvbG9yPSIjZGRkZGRkIi8+PHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjYWFhYWFhIi8+PC9saW5lYXJHcmFkaWVudD48L2RlZnM+PHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0idXJsKCNncmFkKSIgLz48L3N2Zz4g'), url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PHJhZGlhbEdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgY3g9IjUwJSIgY3k9IjUwJSIgcj0iMTAwIj48c3RvcCBvZmZzZXQ9IjAlIiBzdG9wLWNvbG9yPSIjZGRkZGRkIi8+PHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjYWFhYWFhIi8+PC9yYWRpYWxHcmFkaWVudD48L2RlZnM+PHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0idXJsKCNncmFkKSIgLz48L3N2Zz4g');
Expand Down
4 changes: 2 additions & 2 deletions test/fixtures/stylesheets/compass/sass/gradients.sass
Expand Up @@ -23,10 +23,10 @@ $experimental-support-for-svg: true
+background-image(radial-gradient(center center, #ddd, #aaa 100px))

.bg-linear-gradient-with-angle
+background-image(linear-gradient(top left -45deg, #ddd, #aaa))
+background-image(linear-gradient(-45deg, #ddd, #aaa))

.bg-radial-gradient-with-angle-and-shape
+background-image(radial-gradient(center center 45deg, ellipse cover, #ddd, #aaa 100px))
+background-image(radial-gradient(45deg, ellipse cover, #ddd, #aaa 100px))

.bg-all-gradient-types
+background-image(image-url("4x6.png"), linear-gradient(top left, #ddd, #aaa), radial-gradient(center center, #ddd, #aaa 100px))
Expand Down

0 comments on commit ad0e172

Please sign in to comment.