diff --git a/lib/compass/sass_extensions/functions/sprites.rb b/lib/compass/sass_extensions/functions/sprites.rb index 8b6ae8e9..907d8d7f 100644 --- a/lib/compass/sass_extensions/functions/sprites.rb +++ b/lib/compass/sass_extensions/functions/sprites.rb @@ -1,11 +1,12 @@ require 'chunky_png' module Compass::SassExtensions::Functions::Sprites + SASS_NULL = Sass::Script::Number::new(0) + def sprite_image(uri) uri = uri.value path, name = Compass::Sprites.path_and_name(uri) last_spacing = 0 - default_spacing = number_from_var("#{name}-spacing") width = 0 height = 0 images = Compass::Sprites.sprites(name) @@ -14,7 +15,7 @@ module Compass::SassExtensions::Functions::Sprites images.each do |image| current_spacing = number_from_var("#{name}-#{image[:name]}-spacing") if height > 0 - height += [current_spacing, last_spacing, default_spacing].max + height += [current_spacing, last_spacing].max end image[:y] = height height += image[:height] @@ -26,21 +27,32 @@ module Compass::SassExtensions::Functions::Sprites output_png = ChunkyPNG::Image.new(width, height, ChunkyPNG::Color::TRANSPARENT) images.each do |image| input_png = ChunkyPNG::Image.from_file(image[:file]) - x = 0 - y = image[:y] - output_png.replace input_png, x, y + position = environment.var("#{name}-#{image[:name]}-position") + if position.unit_str == "%" + image[:x] = (width - image[:width]) * (position.value / 100) + else + image[:x] = position.value + end + output_png.replace input_png, image[:x], image[:y] end output_png.save File.join(File.join(Compass.configuration.images_path, "#{path}.png")) image_url(Sass::Script::String.new("#{path}.png")) end - def sprite_position(file) + def sprite_position(file, x_shift = SASS_NULL, y_shift = SASS_NULL) name = File.dirname(file.value) - sprite = File.basename(file.value, '.png') - y = Compass::Sprites.sprites(name).detect{ |sprite_info| sprite_info[:name] == sprite }[:y] - y = "-#{y}px" unless y == 0 - Sass::Script::String.new("0 #{y}") + image_name = File.basename(file.value, '.png') + image = Compass::Sprites.sprites(name).detect{ |image| image[:name] == image_name } + if x_shift.unit_str == "%" + x = x_shift.to_s + else + x = x_shift.value - image[:x] + x = "#{x}px" unless x == 0 + end + y = y_shift.value - image[:y] + y = "#{y}px" unless y == 0 + Sass::Script::String.new("#{x} #{y}") end private diff --git a/lib/compass/sprites.rb b/lib/compass/sprites.rb index 09ee3c2f..0e90b974 100644 --- a/lib/compass/sprites.rb +++ b/lib/compass/sprites.rb @@ -42,6 +42,15 @@ module Compass contents = <<-SCSS $#{name}-sprite-base-class: ".#{name}-sprite" !default; $#{name}-sprite-dimensions: false !default; + $#{name}-position: 0% !default; + $#{name}-spacing: 0 !default; + + #{images.map do |sprite| + <<-SCSS + $#{name}-#{sprite[:name]}-position: $#{name}-position !default; + $#{name}-#{sprite[:name]}-spacing: $#{name}-spacing !default; + SCSS + end.join} \#{$#{name}-sprite-base-class} { background: sprite-image("#{uri}") no-repeat; @@ -52,13 +61,13 @@ module Compass width: image-width("#{name}/\#{$sprite}.png"); } - @mixin #{name}-sprite-position($sprite) { - background-position: sprite-position("#{path}/\#{$sprite}.png"); + @mixin #{name}-sprite-position($sprite, $x: 0, $y: 0) { + background-position: sprite-position("#{path}/\#{$sprite}.png", $x, $y); } - @mixin #{name}-sprite($sprite, $dimensions: $#{name}-sprite-dimensions) { + @mixin #{name}-sprite($sprite, $dimensions: $#{name}-sprite-dimensions, $x: 0, $y: 0) { @extend \#{$#{name}-sprite-base-class}; - @include #{name}-sprite-position($sprite); + @include #{name}-sprite-position($sprite, $x, $y); @if $dimensions { @include #{name}-sprite-dimensions($sprite); } @@ -66,8 +75,12 @@ module Compass @mixin all-#{name}-sprites { #{images.map do |sprite| - %Q(.#{name}-#{sprite[:name]} { @include #{name}-sprite("#{sprite[:name]}"); }) - end.join} + <<-SCSS + .#{name}-#{sprite[:name]} { + @include #{name}-sprite("#{sprite[:name]}"); + } + SCSS + end.join} } SCSS options.merge! :filename => name, :syntax => :scss, :importer => self diff --git a/spec/sprites_spec.rb b/spec/sprites_spec.rb index df83aeea..f899123c 100644 --- a/spec/sprites_spec.rb +++ b/spec/sprites_spec.rb @@ -1,5 +1,6 @@ require File.expand_path(File.dirname(__FILE__) + '/spec_helper') require "compass/sprites" +require 'digest/md5' describe Compass::Sprites do @@ -19,6 +20,12 @@ describe Compass::Sprites do def image_size(file) IO.read(File.join(@images_tmp_path, file))[0x10..0x18].unpack('NN') end + + def image_md5(file) + md5 = Digest::MD5.new + md5.update IO.read(File.join(@images_tmp_path, file)) + md5.hexdigest + end def render(scss) scss = %Q(@import "compass"; #{scss}) @@ -53,6 +60,7 @@ describe Compass::Sprites do } CSS image_size('squares.png').should == [20, 30] + image_md5('squares.png').should == 'ac68084abb8f43794a75c5fb28bd62fe' end it "should generate sprite classes with dimensions" do @@ -125,7 +133,7 @@ describe Compass::Sprites do CSS image_size('squares.png').should == [20, 30] end - + it "should calculate the spacing between images but not before first image" do css = render <<-SCSS $squares-10x10-spacing: 33px; @@ -147,7 +155,7 @@ describe Compass::Sprites do CSS image_size('squares.png').should == [20, 63] end - + it "should calculate the spacing between images" do css = render <<-SCSS $squares-20x20-spacing: 33px; @@ -169,7 +177,7 @@ describe Compass::Sprites do CSS image_size('squares.png').should == [20, 63] end - + it "should calculate the maximum spacing between images" do css = render <<-SCSS $squares-10x10-spacing: 44px; @@ -192,7 +200,7 @@ describe Compass::Sprites do CSS image_size('squares.png').should == [20, 74] end - + it "should calculate the maximum spacing between images in reversed order" do css = render <<-SCSS $squares-10x10-spacing: 33px; @@ -215,7 +223,7 @@ describe Compass::Sprites do CSS image_size('squares.png').should == [20, 74] end - + it "should calculate the default spacing between images" do css = render <<-SCSS $squares-spacing: 22px; @@ -238,4 +246,80 @@ describe Compass::Sprites do image_size('squares.png').should == [20, 52] end + it "should use position adjustments in functions" do + css = render <<-SCSS + $squares-position: 100%; + @import "squares/*.png"; + + .adjusted-percentage { + background-position: sprite-position("squares/10x10.png", 100%); + } + + .adjusted-px-1 { + background-position: sprite-position("squares/10x10.png", 4px); + } + + .adjusted-px-2 { + background-position: sprite-position("squares/20x20.png", -3px, 2px); + } + SCSS + css.should == <<-CSS + .squares-sprite { + background: url('/squares.png') no-repeat; + } + + .adjusted-percentage { + background-position: 100% 0; + } + + .adjusted-px-1 { + background-position: -6px 0; + } + + .adjusted-px-2 { + background-position: -3px -8px; + } + CSS + image_size('squares.png').should == [20, 30] + image_md5('squares.png').should == 'e274a620ff44c14774fa470d0a9020a1' + end + + it "should use position adjustments in mixins" do + css = render <<-SCSS + $squares-position: 100%; + @import "squares/*.png"; + + .adjusted-percentage { + @include squares-sprite("10x10", $x: 100%); + } + + .adjusted-px-1 { + @include squares-sprite("10x10", $x: 4px); + } + + .adjusted-px-2 { + @include squares-sprite("20x20", $x: -3px, $y: 2px); + } + SCSS + css.should == <<-CSS + .squares-sprite, .adjusted-percentage, .adjusted-px-1, .adjusted-px-2 { + background: url('/squares.png') no-repeat; + } + + .adjusted-percentage { + background-position: 100% 0; + } + + .adjusted-px-1 { + background-position: -6px 0; + } + + .adjusted-px-2 { + background-position: -3px -8px; + } + CSS + image_size('squares.png').should == [20, 30] + image_md5('squares.png').should == 'e274a620ff44c14774fa470d0a9020a1' + end + end \ No newline at end of file