Closed siiron closed 9 years ago
The quick answer is sprite-position
in spritesmith
is unintentionally similarly named. While compass
was part of the inspiration for spritesmith
(i.e. making spritesheets sane), we did the CSS architecture independently =/
That being said, I am interested in learning about your use case more. I am going to perform some research to see how compass
implements this feature.
Alright, I am done with my research. Unfortunately, compass
relies on folders so I cannot point you to my work. As a result, I will attempt to compensate here.
I used 2 test sprites as our images, located in images/my-icons/
(e.g. images/my-icons/sprite1.png
):
https://github.com/Ensighten/grunt-spritesmith/blob/5.0.0/src-test/test_files/sprite1.png
https://github.com/Ensighten/grunt-spritesmith/blob/5.0.0/src-test/test_files/sprite3.png
I defined a sass/screen.scss
via compass init
. Its contents were:
@import "compass/utilities/sprites";
@import "my-icons/*.png";
.sprite1 {
@include my-icons-sprite(sprite1);
}
.sprite3 {
@include my-icons-sprite(sprite3);
}
Upon compilation, it's output was:
/* line 56, my-icons/*.png */
.my-icons-sprite, .sprite1, .sprite3 {
background-image: url('/images/my-icons-sc7a02fc51a.png');
background-repeat: no-repeat;
}
/* line 4, ../sass/screen.scss */
.sprite1 {
background-position: 0 0;
}
/* line 8, ../sass/screen.scss */
.sprite3 {
background-position: 0 -50px;
}
When I moved to using my-icons-background-position
:
@import "compass/utilities/sprites";
@import "my-icons/*.png";
.sprite1 {
@include my-icons-sprite(sprite1);
@include my-icons-sprite-position(sprite1, 20px, 20px);
}
.sprite3 {
@include my-icons-sprite(sprite3);
@include my-icons-sprite-position(sprite1, 20px, 20px);
}
I received the same spritesheet with the following CSS:
/* line 56, my-icons/*.png */
.my-icons-sprite, .sprite1, .sprite3 {
background-image: url('/images/my-icons-sc7a02fc51a.png');
background-repeat: no-repeat;
}
/* line 4, ../sass/screen.scss */
.sprite1 {
background-position: 0 0;
background-position: 20px 20px;
}
/* line 9, ../sass/screen.scss */
.sprite3 {
background-position: 0 -50px;
background-position: 20px 20px;
}
Now that we are on the same page, we can reflect on our options. I am leaning towards not adding this feature for a few reasons:
include
does nothing relative to the sprite itself (e.g. 20px
doesn't add 20px
to -50px
to yield -30px
for sprite3
). Since it works in absolutes, it would be simpler to define our background-position
by hand for what we want.As a consequence of these reasons, I am deciding to not implement the feature. The suggested workaround will be using the offset-*
variables you mentioned:
https://github.com/twolfson/spritesheet-templates/blob/10.0.0/test/expected_files/scss.scss
https://github.com/twolfson/spritesheet-templates/blob/10.0.0/test/expected_files/scss.scss#L73-L75
// Compass flavor
@include my-icons-sprite-position(sprite1, 20px, 20px);
// spritesmith equvialent
background-position: 20px 20px;
// or with custom variables
background-position: $sprite1-offset-x $sprite1-offset-y;
I should clarify on the point I alluded to about "padding" vs "other sprites". To push a sprite down with some whitespace, we typically suggest including this as a part of the sprite.
It costs a few extra bytes but makes everything sane in terms of CSS (e.g. no browser specific padding fidgeting). Additionally, it allows us to use non-linear packing algorithms which saves much more bytes.
Thank you for looking in to this.
Here is a little more detail about my issue (using a top-down algorithm with padding of 12px between the icons in the sprite):
Using the following Sass:
@include sprite-image($icon-foo);
@include sprite-position($icon-foo);
background-repeat: no-repeat;
gives me the following CSS:
background-image: url(sprite.png);
background-position: 0px -108px;
background-repeat: no-repeat;
This looks like this:
As you can see, I'd like to have the icon placed to right and a couple of pixels down.
I guess it's ok to just insert the overrides by either redefining the variabel or use Sass' calculations to calculate the adjustment relative to the original value of the generated offset:
@include sprite-image($icon-foo);
background-position: 100% $icon-foo-offset-y + 2;
background-repeat: no-repeat;
This would generate the following CSS:
background-image: url(sprite.png);
backround-position: 100% -106px;
background-repeat: no-repeat;
Problem solved I guess.
My main concern was to hand-code these values and not being able to dynamically calculate them if the sprite changes (adding or deleting icons in the icon-set).
The x-position 100% is still hard coded, but the y-position will always nudge the icon 2px down, even if the original value changes.
What I really ended up doing, was to use css calc
to add some pixels to the 100% x-position. This way I could compensate for the exra pixels the icon had to its right (as part of the spritesheet):
@include sprite-image($icon-foo);
background-position: calc(100% + 4px) $icon-foo-offset-y + 2;
background-repeat: no-repeat;
This got me this CSS:
background-image: url(sprite.png);
backround-position: calc(100% + 4px) -106px;
background-repeat: no-repeat;
And this result:
Voila!
Ah, thanks for the clarification on your use case =)
One thing to note is calc
has problems with IE9 and no support in IE8/below:
http://caniuse.com/#search=calc
If you need to support those browser, I suggest creating 2 separate spritesheets (via 2 grunt tasks), 1 exclusively for image background images (which hopefully are the same width) and 1 for everything else.
The background images can be vertically stacked as they require but the others can be packed more efficiently via the binary-tree
algorithm.
One more option we can consider is using a top-down
algorithm but that right-aligns all images. This can be done by specifying an object to the algorithm
option:
https://github.com/Ensighten/grunt-spritesmith/tree/5.0.0#documentation
https://github.com/twolfson/layout/blob/2.2.0/lib/algorithms/top-down.algorithm.js
{
algorithm: {
sort: function (items) {
// Sort the items by their height
items.sort(function (a, b) {
return a.height - b.height;
});
return items;
},
placeItems: function (items) {
// Find the maximum width
var maxWidth = items.reduce(function (maxWidth, item) {
if (item.width > maxWidth) {
return item.width;
}
return maxWidth;
}, 0);
// Iterate over each of the items
var y = 0;
items.forEach(function (item) {
// Update the y to the current height
item.x = maxWidth - item.width;
item.y = y;
// Increment the y by the item's height
y += item.height;
});
// Return the items
return items;
};
}
}
First of all – great job on spritesmith! I totally dig it, and currently I’m rewriting our current compass-generated sprites to use grunt-spritesmith instead.
I’m still a bit confused about position overrides.
I'm struggling to figure out how to override the generated values for natural background-position. By that I mean moving the position relative of the generated values. I'm used to Compass' mixin sprite-background-position. This can take parameters for offset, but trying this with the mixin
sprite-position
in grunt-spritesmith:gives me the following error: "Mixin sprite-position takes 1 argument but 3 were passed".
Usually the generated sprite-position is fine, but in some cases I find the need to adjust it by a pixel or two. And often there is only need to do this for a specific icon where other icons in the sprite is fine as they are.
The most common use-case I can think of is when you have an element, e.g. an input field. You'd like to show an icon inside the input and on the right. With the algorithm set to top-down, you'd get a nice vertical column-shaped spritemap. Since the width of the element should not be controlled by the generated sprite (not generating values for width/ height), you'd use:
If you'd add:
the icon is positioned at the left in the input-field. Not quite what you'd want. You'd like it on the right.
I'd expect something like this:
100%
represents the x-position override (100% to the right), and the$icon-foo-offset-y
uses the dynamic generated y-position.How can this be done?