New background image mixin with gradient support and up to 10 images.

This commit is contained in:
Chris Eppstein 2010-11-13 19:19:41 -08:00
parent 1fe22d7257
commit 9aea35738d
6 changed files with 219 additions and 23 deletions

View File

@ -1,6 +1,24 @@
@import "shared"; @import "shared";
@import "compass/utilities/general/hacks"; @import "compass/utilities/general/hacks";
@mixin background-image(
$image-1, $image-2: false, $image-3: false,
$image-4: false, $image-5: false, $image-6: false,
$image-7: false, $image-8: false, $image-9: false, $image-10: false
) {
$images: compact($image-1, $image-2, $image-3, $image-4 ,$image-5, $image-6, $image-7, $image-8, $image-9, $image-10);
@if $experimental-support-for-webkit and prefixed(-webkit, $images) {
background-image: -webkit($images);
}
@if $experimental-support-for-mozilla and prefixed(-moz, $images) {
background-image: -moz($images);
}
@if $experimental-support-for-svg and prefixed(-svg, $images) {
background-image: -svg($images);
}
background-image: $images;
}
// The linear gradient mixin works best across browsers if you use percentage-based color stops. // The linear gradient mixin works best across browsers if you use percentage-based color stops.
// //
// Examples: // Examples:
@ -50,7 +68,7 @@
@if $experimental-support-for-mozilla { @if $experimental-support-for-mozilla {
background-image: #{$background}-moz-linear-gradient($start, $color-stops); background-image: #{$background}-moz-linear-gradient($start, $color-stops);
} }
background-image: #{$background}linear-gradient($start, $color-stops); background-image: #{$background}#{linear}-gradient($start, $color-stops);
} }
// Emit a IE-Specific filters that renders a simple linear gradient. // Emit a IE-Specific filters that renders a simple linear gradient.
@ -104,5 +122,5 @@
@if $experimental-support-for-mozilla { @if $experimental-support-for-mozilla {
background-image: #{$background}-moz-radial-gradient($center-position, circle, $color-stops); background-image: #{$background}-moz-radial-gradient($center-position, circle, $color-stops);
} }
background-image: #{$background}radial-gradient($center-position, circle, $color-stops); background-image: #{$background}#{radial}-gradient($center-position, circle, $color-stops);
} }

View File

@ -3,8 +3,8 @@ end
%w( %w(
selectors enumerate urls display if selectors enumerate urls display if
inline_image image_size gradient_support inline_image image_size constants gradient_support
font_files constants lists colors trig font_files lists colors trig
).each do |func| ).each do |func|
require "compass/sass_extensions/functions/#{func}" require "compass/sass_extensions/functions/#{func}"
end end

View File

@ -9,13 +9,16 @@ module Compass::SassExtensions::Functions::GradientSupport
values.map{|v| v.inspect}.join(", ") values.map{|v| v.inspect}.join(", ")
end end
def to_s def to_s
inspect values.map{|v| v.to_s}.join(", ")
end end
end end
class ColorStop < Sass::Script::Literal class ColorStop < Sass::Script::Literal
attr_accessor :color, :stop attr_accessor :color, :stop
def initialize(color, stop = nil) def initialize(color, stop = nil)
unless Sass::Script::Color === color || Sass::Script::Funcall === color
raise Sass::SyntaxError, "Expected a color. Got: #{color}"
end
self.color, self.stop = color, stop self.color, self.stop = color, stop
end end
def inspect def inspect
@ -35,7 +38,103 @@ module Compass::SassExtensions::Functions::GradientSupport
end end
end end
class RadialGradient < Sass::Script::Literal
attr_accessor :position_or_angle, :shape_or_size, :color_stops
def initialize(position_or_angle, shape_or_size, color_stops)
unless color_stops.values.size >= 2
raise Sass::SyntaxError, "At least two color stops are required for a radial-gradient"
end
self.position_or_angle = position_or_angle
self.shape_or_size = shape_or_size
self.color_stops = color_stops
end
def inspect
to_s
end
def to_s
s = "radial-gradient("
s << position_or_angle.to_s << ", " if position_or_angle
s << shape_or_size.to_s << ", " if shape_or_size
s << color_stops.to_s
s << ")"
end
def to_webkit
args = [
grad_point(position_or_angle),
"0",
grad_point(position_or_angle),
grad_end_position(color_stops, Sass::Script::Bool.new(true)),
grad_color_stops(color_stops)
]
Sass::Script::String.new("-webkit-gradient(radial, #{args.join(', ')})")
end
def to_moz
Sass::Script::String.new("-moz-#{to_s}")
end
def to_svg
# XXX Add shape support if possible
radial_svg_gradient(color_stops, position_or_angle)
end
end
class LinearGradient < Sass::Script::Literal
attr_accessor :color_stops, :position_or_angle
def initialize(position_or_angle, color_stops)
unless color_stops.values.size >= 2
raise Sass::SyntaxError, "At least two color stops are required for a linear-gradient"
end
self.position_or_angle = position_or_angle
self.color_stops = color_stops
end
def inspect
to_s
end
def to_s
s = "linear-gradient("
s << position_or_angle.to_s << ", " if position_or_angle
s << color_stops.to_s
s << ")"
end
def to_webkit
args = [
grad_point(position_or_angle),
grad_point(opposite_position(position_or_angle)),
grad_color_stops(color_stops)
]
Sass::Script::String.new("-webkit-gradient(linear, #{args.join(', ')})")
end
def to_moz
Sass::Script::String.new("-moz-#{to_s}")
end
def to_svg
linear_svg_gradient(color_stops, position_or_angle)
end
end
module Functions module Functions
def radial_gradient(position_or_angle, shape_or_size, *color_stops)
if color_stop?(shape_or_size)
color_stops.unshift(shape_or_size)
shape_or_size = nil
end
if color_stop?(position_or_angle)
color_stops.unshift(position_or_angle)
position_or_angle = nil
end
RadialGradient.new(position_or_angle, shape_or_size, send(:color_stops, *color_stops))
end
def linear_gradient(position_or_angle, *color_stops)
if color_stop?(position_or_angle)
color_stops.unshift(position_or_angle)
position_or_angle = nil
end
LinearGradient.new(position_or_angle, send(:color_stops, *color_stops))
end
# 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)
stops = color_stops_in_percentages(color_list).map do |stop, color| stops = color_stops_in_percentages(color_list).map do |stop, color|
@ -132,24 +231,7 @@ module Compass::SassExtensions::Functions::GradientSupport
when Sass::Script::String when Sass::Script::String
# We get a string as the result of concatenation # We get a string as the result of concatenation
# So we have to reparse the expression # So we have to reparse the expression
color = stop = nil parse_color_stop(arg)
expr = Sass::Script::Parser.parse(arg.value, 0, 0)
case expr
when Sass::Script::Color
color = expr
when Sass::Script::Funcall
color = expr
when Sass::Script::Operation
unless expr.instance_variable_get("@operator") == :concat
# This should never happen.
raise Sass::SyntaxError, "Couldn't parse a color stop from: #{arg.value}"
end
color = expr.instance_variable_get("@operand1")
stop = expr.instance_variable_get("@operand2")
else
raise Sass::SyntaxError, "Couldn't parse a color stop from: #{arg.value}"
end
ColorStop.new(color, stop)
else else
raise Sass::SyntaxError, "Not a valid color stop: #{arg}" raise Sass::SyntaxError, "Not a valid color stop: #{arg}"
end end
@ -174,6 +256,26 @@ module Compass::SassExtensions::Functions::GradientSupport
inline_image_string(svg.gsub(/\s+/, ' '), 'image/svg+xml') inline_image_string(svg.gsub(/\s+/, ' '), 'image/svg+xml')
end end
# Returns a comma-delimited list after removing any non-true values
def compact(*args)
List.new(*args.reject{|a| !a.to_bool})
end
%w(webkit moz o ms svg).each do |prefix|
class_eval <<-RUBY
def _#{prefix}(*args)
args.map!{|a| a.is_a?(List) ? a.values : a}.flatten!
List.new(*args.map!{|a| a.respond_to?(:to_#{prefix}) ? a.to_#{prefix} : a})
end
RUBY
end
def prefixed(prefix, *args)
method = prefix.value.sub(/^-/,"to_").to_sym
args.map!{|a| a.is_a?(List) ? a.values : a}.flatten!
Sass::Script::Bool.new(args.any?{|a| a.respond_to?(method)})
end
private private
def normalize_stops!(color_list) def normalize_stops!(color_list)
positions = color_list.values positions = color_list.values
@ -208,11 +310,41 @@ module Compass::SassExtensions::Functions::GradientSupport
end end
nil nil
end end
def assert_list(value) def assert_list(value)
return if value.is_a?(List) return if value.is_a?(List)
raise ArgumentError.new("#{value.inspect} is not a list of color stops. Expected: color_stops(<color> <number>?, ...)") raise ArgumentError.new("#{value.inspect} is not a list of color stops. Expected: color_stops(<color> <number>?, ...)")
end end
def parse_color_stop(arg)
return ColorStop.new(arg) if arg.is_a?(Sass::Script::Color)
return nil unless arg.is_a?(Sass::Script::String)
color = stop = nil
expr = Sass::Script::Parser.parse(arg.value, 0, 0)
case expr
when Sass::Script::Color
color = expr
when Sass::Script::Funcall
color = expr
when Sass::Script::Operation
unless [:concat, :space].include?(expr.instance_variable_get("@operator"))
# This should never happen.
raise Sass::SyntaxError, "Couldn't parse a color stop from: #{arg.value}"
end
color = expr.instance_variable_get("@operand1")
stop = expr.instance_variable_get("@operand2")
else
raise Sass::SyntaxError, "Couldn't parse a color stop from: #{arg.value}"
end
ColorStop.new(color, stop)
end
def color_stop?(arg)
parse_color_stop(arg)
rescue
nil
end
def linear_svg(color_stops, x1, y1, x2, y2) def linear_svg(color_stops, x1, y1, x2, y2)
gradient = <<-EOG gradient = <<-EOG
<linearGradient id="grad" x1="#{x1}" y1="#{y1}" x2="#{x2}" y2="#{y2}"> <linearGradient id="grad" x1="#{x1}" y1="#{y1}" x2="#{x2}" y2="#{y2}">
@ -248,4 +380,14 @@ module Compass::SassExtensions::Functions::GradientSupport
EOS EOS
end end
end end
class LinearGradient < Sass::Script::Literal
include Functions
include Compass::SassExtensions::Functions::Constants
include Compass::SassExtensions::Functions::InlineImage
end
class RadialGradient < Sass::Script::Literal
include Functions
include Compass::SassExtensions::Functions::Constants
include Compass::SassExtensions::Functions::InlineImage
end
end end

View File

@ -1,6 +1,8 @@
require 'test_helper' require 'test_helper'
require 'fileutils' require 'fileutils'
require 'compass' require 'compass'
require 'compass/logger'
require 'sass/plugin'
class CompassTest < Test::Unit::TestCase class CompassTest < Test::Unit::TestCase
include Compass::TestCaseHelper include Compass::TestCaseHelper

View File

@ -1,3 +1,24 @@
.bg-simple-image {
background-image: url("foo.png"); }
.bg-linear-gradient {
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 0%, #aaaaaa 100%);
background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+IDxkZWZzPiA8bGluZWFyR3JhZGllbnQgaWQ9ImdyYWQiIHgxPSIwJSIgeTE9IjAlIiB4Mj0iMTAwJSIgeTI9IjEwMCUiPiA8c3RvcCBvZmZzZXQ9IjAlIiBzdG9wLWNvbG9yPSIjZGRkZGRkIiAvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2FhYWFhYSIgLz4gPC9saW5lYXJHcmFkaWVudD4gPC9kZWZzPiA8cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI2dyYWQpIiAvPiA8L3N2Zz4g');
background-image: linear-gradient(top left, #dddddd 0%, #aaaaaa 100%); }
.bg-radial-gradient {
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, #dddddd 0%, #aaaaaa 100px);
background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+IDxkZWZzPiA8cmFkaWFsR3JhZGllbnQgaWQ9ImdyYWQiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiBjeD0iNTAlIiBjeT0iNTAlIiByPSIxMDAiPiA8c3RvcCBvZmZzZXQ9IjAlIiBzdG9wLWNvbG9yPSIjZGRkZGRkIiAvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2FhYWFhYSIgLz4gPC9yYWRpYWxHcmFkaWVudD4gPC9kZWZzPiA8cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI2dyYWQpIiAvPiA8L3N2Zz4g');
background-image: radial-gradient(center center, #dddddd 0%, #aaaaaa 100px); }
.bg-all-gradient-types {
background-image: url('/images/4x6.png?busted=true'), -webkit-gradient(linear, 0% 0%, 100% 100%, color-stop(0%, #dddddd), color-stop(100%, #aaaaaa)), -webkit-gradient(radial, 50% 50%, 0, 50% 50%, 100, color-stop(0%, #dddddd), color-stop(100%, #aaaaaa));
background-image: url('/images/4x6.png?busted=true'), -moz-linear-gradient(top left, #dddddd 0%, #aaaaaa 100%), -moz-radial-gradient(center center, #dddddd 0%, #aaaaaa 100px);
background-image: url('/images/4x6.png?busted=true'), url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+IDxkZWZzPiA8bGluZWFyR3JhZGllbnQgaWQ9ImdyYWQiIHgxPSIwJSIgeTE9IjAlIiB4Mj0iMTAwJSIgeTI9IjEwMCUiPiA8c3RvcCBvZmZzZXQ9IjAlIiBzdG9wLWNvbG9yPSIjZGRkZGRkIiAvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2FhYWFhYSIgLz4gPC9saW5lYXJHcmFkaWVudD4gPC9kZWZzPiA8cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI2dyYWQpIiAvPiA8L3N2Zz4g'), url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+IDxkZWZzPiA8cmFkaWFsR3JhZGllbnQgaWQ9ImdyYWQiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiBjeD0iNTAlIiBjeT0iNTAlIiByPSIxMDAiPiA8c3RvcCBvZmZzZXQ9IjAlIiBzdG9wLWNvbG9yPSIjZGRkZGRkIiAvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2FhYWFhYSIgLz4gPC9yYWRpYWxHcmFkaWVudD4gPC9kZWZzPiA8cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI2dyYWQpIiAvPiA8L3N2Zz4g');
background-image: url('/images/4x6.png?busted=true'), linear-gradient(top left, #dddddd 0%, #aaaaaa 100%), radial-gradient(center center, #dddddd 0%, #aaaaaa 100px); }
.linear-1 { .linear-1 {
background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #dddddd), color-stop(100%, #aaaaaa)); background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #dddddd), color-stop(100%, #aaaaaa));
background-image: -moz-linear-gradient(top, #dddddd 0%, #aaaaaa 100%); background-image: -moz-linear-gradient(top, #dddddd 0%, #aaaaaa 100%);

View File

@ -1,5 +1,18 @@
@import compass/css3 @import compass/css3
$experimental-support-for-svg: true
.bg-simple-image
+background-image(url('foo.png'))
.bg-linear-gradient
+background-image(linear-gradient(top left, #ddd, #aaa))
.bg-radial-gradient
+background-image(radial-gradient(center center, #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))
$experimental-support-for-svg: false
.linear-1 .linear-1
+linear-gradient(color-stops(#dddddd, #aaaaaa)) +linear-gradient(color-stops(#dddddd, #aaaaaa))