Sprites LESS CSS Variable increment issue
Strictly Speaking You Cannot
Variables in LESS are essentially constants once defined in a particular scope, and so cannot be changed (including incremented). So your @counter: @index + 1;
is not incrementing the global variable at all, but rather creating a new value for a local scope @counter
variable inside that particular call of .myIconX()
. See the documentation on how variables work in LESS.
Emulated by Recursive Local Variable Setting
This works, based off information deemed a bug here, but which I do not believe is strictly speaking a bug. At any rate, it can be utilized to meet your needs like so (I just implemented an @row: 1
and tweaked some code to show the calculation working):
@row: 1;
.init() {
.inc-impl(1);
} .init();
.inc-impl(@new) {
.redefine() {
@counter: @new;
}
}
#my-icon-bundle {
.my-icons () {
#my-icon-bundle .myIconX("classX1", @counter);
#my-icon-bundle .myIconX("classYY1", @counter);
}
.myIconX(@name) {
.redefine();
.inc-impl((@counter + 1));
@nameText: ~".my-icon-@{name}";
@{nameText} { #my-icon-bundle .myIcon(@row); }
}
.myIcon(@row) {
@x: @row * @counter;
@y: @row * @counter;
background-position: -@x -@y;
}
}
#my-icon-bundle .myIconX("classX1");
#my-icon-bundle .myIconX("classX1");
#my-icon-bundle .myIconX("classYY1");
Output CSS is:
.my-icon-classX1 {
background-position: -1 -1;
}
.my-icon-classX1 {
background-position: -2 -2;
}
.my-icon-classYY1 {
background-position: -3 -3;
}
This demonstrates that with each call of the .myIconX()
mixin, it is setting the counter by +1
for the next call.
Warning: Whether this solution is based on buggy behavior or not is questionable, but if it is a bug, this solution may disappear in the future. For further comments on the limitations of this method, see the discussion here.
Increment a variable in LESS css
Not Strictly Possible
See the documentation on LESS variables. Essentially, LESS variables are constants in the scope of their creation. They are lazy loaded, and cannot be "changed" in that way. The very last definition will be the one used for all in that scope. In your case an error will occur, because variables cannot reference themselves.
Consider this example:
@counter: 1;
.someSelector("nameOfClass", @counter);
@counter: 2;
.someSelector("nameOfClass1", @counter);
.someSelector(@name; @count) {
@className: ~"@{name}";
.@{className} {
test: @count;
}
}
The output will be 2
for both:
.nameOfClass {
test: 2;
}
.nameOfClass1 {
test: 2;
}
This is because LESS defines the @counter
with the last definition of the variable in that scope. It does not pay attention to the order of the calls using @counter
, but rather acts much like CSS and takes the "cascade" of the variable into consideration.
For further discussion of this in LESS, you might track discussion that occurs on this LESS feature request.
Solution is in Recursive Call Setter for the Local Variable
Seven-phases-max linked to what he believes to be a bug in LESS, but I don't think it is. Rather, it appears to me to be a creative use of recursive resetting of the counter to get the effect desired. This allows for you to achieve what you desire like so (using my example code):
// counter
.init() {
.inc-impl(1); // set initial value
} .init();
.inc-impl(@new) {
.redefine() {
@counter: @new;
}
}
.someSelector(@name) {
.redefine(); // this sets the value of counter for this call only
.inc-impl((@counter + 1)); // this sets the value of counter for the next call
@className: ~"@{name}";
.@{className} {
test: @counter;
}
}
.someSelector("nameOfClass");
.someSelector("nameOfClass1");
Here is the CSS output:
.nameOfClass {
test: 1;
}
.nameOfClass1 {
test: 2;
}
NOTE: I believe you are not strictly changing a global value here, but rather setting a new local value with each call to .someSelector
. Whether this is based on buggy behavior or not is questionable, but if so, this solution may disappear in the future.
For further comments of the limitations of this method, see the discussion here.
Auto-generate LESS styles for sprite icons
Pure LESS (assuming you're using Web Essentials 2013 which uses LESS 1.5.x):
@icons: upvote, downvote, comment, new, notify, search, popup, eye, cross;
.iconize();
.iconize(@i: length(@icons)) when (@i > 0) {
.iconize((@i - 1));
@value: extract(@icons, @i); // LESS arrays are 1-based
.@{value} {background-position: (-20px * (@i - 1)) 0}
.color.@{value} {background-position: (-20px * (@i - 1)) -20px}
.white.@{value} {background-position: (-20px * (@i - 1)) -40px}
}
I removed &
from selector names since it has no effect when you generate these classes in the global scope (but put it back if you actually need .iconize
to be nested in another ruleset). It is also possible to calculate array length in earlier LESS versions (that have no length
function) w/o any javascript, but I don't list this method here since it's quite scary (and you don't need it anyway).
Your javascript based loop is in fact less or more correct but the problem is all values returned by LESS inline javascript are of so-called "anonymous value" type and not a numbers so that when (@c < @count)
condition is always true and the loop becomes infinite. (basically the condition is expanded exactly as when (0 < ~'9')
... when (9 < ~'9')
= true etc.)
Converting from width to background-width in Less for sprites within CSS class
Just after posting this I looked into where the sprite function came from and found my answer in the generated sprite.less file:
.sprite-width(@sprite) {
width: ~`"@{sprite}".split(', ')[4]`;
}
.sprite-height(@sprite) {
height: ~`"@{sprite}".split(', ')[5]`;
}
The sprite.less file is generated using a mustache template. In order to solve my problem in Spritesmith you can specify a mustache template. It was also incorrect that I was looking at background-width and the property I needed was background-size. My attempts at inlining also failed, and I should have been looking at using a div rather than sizing the sprite component.
How to write it short in less?
If the usage is <i class="icon-gps" ></i>
i.icon
{
&-gps {
.sprite(@icon-gps);
display:inline-block;
}
&-home {
.sprite(@icon-home);
display:inline-block;
}
&-map {
.sprite(@icon-map);
display:inline-block;
}
}
If the usage is <i class="icon icon-gps"></i>
(thanks to @Curt's comment)
i.icon
{
display:inline-block;
&-gps {
.sprite(@icon-gps);
}
&-home {
.sprite(@icon-home);
}
&-map {
.sprite(@icon-map);
}
}
LESS File Extension Variable
Since Modernizr adds classes to the html
element for properties supported, and in this case is adding the svg
class, it seems like the easiest way to handle the switch in LESS is to do this in your mixin:
.sprite(@x, @y) {
background: url(../img/sprite.png) no-repeat;
background-position: -(@x*@sprite-Grid) -(@y*@sprite-Grid);
.svg & {
background: url(../img/sprite.svg) no-repeat;
}
}
Then when it is used (assuming here @sprite-Grid: 23px;
), this LESS:
.testClass {
.sprite(10,10);
}
Produces this CSS:
.testClass {
background: url(../img/sprite.png) no-repeat;
background-position: -230px -230px;
}
.svg .testClass {
background: url(../img/sprite.svg) no-repeat;
}
So it appends the .svg
class selector above the whole chain of your selectors, thereby overriding the png
with the svg
when it is available. This is going to increase your overall size on the CSS file, but does allow the LESS file to be precompiled and prevents you from having to replace the path on the fly for the background images.
Javascript setTimeout scope referencing wrong variables
setAnim
looks like it wasn't declared, meaning it's a global variable. This means all your calls to loopAnim
are using and overwriting the same timer ID reference.
Animation with Sprites HTML5
I'm not going to answer your question directly because there simply isn't enough working code in your question to easily diagnose the problem.
Based on your description, most likely you aren't clipping the sprite sheet well enough. You need to provide the x/y/w/h coordinates of the piece of the sprite sheet your want to use. It sounds like you aren't doing that.
Here's a very simply working example of animating a sprite on an HTML5 canvas. Note that this will not work in < IE10.
$(function(){ var spritesheet = new Image(), canvas = $("#game")[0].getContext('2d'), spriteWidth = 64, spriteHeight = 64, indexX = 0, indexX_max = 7, indexY = 11, lastFrameUpdate = 0, frameRate = 1000 / 16, // 16 frames per second (calculated in milliseconds) destX = 0, destY = 0; spritesheet.src = 'http://i.stack.imgur.com/X2Dt6.png'; function drawNextSprite(){ var currentTime = Date.now(), timeSinceLastUpdate = currentTime - lastFrameUpdate, spriteX, spriteY; // If it's been less than 1/16 of a second since the last update, then // don't update the sprite yet if ( timeSinceLastUpdate < frameRate ){ window.requestAnimationFrame(drawNextSprite); return; } lastFrameUpdate = currentTime; if ( indexX > indexX_max ) indexX = 0; if ( destX > 80 ) destX = -spriteWidth; // find the next sprite image to use spriteX = spriteWidth * indexX; spriteY = spriteHeight * indexY; // clear the area we last drew on (so that we don't leave a trail) canvas.clearRect( destX, destY, spriteWidth, spriteHeight ); canvas.drawImage( spritesheet, // the spritesheet spriteX, spriteY, spriteWidth, spriteHeight, // where is the sprite frame? destX, destY, spriteWidth, spriteHeight // where should we draw it on the canvas? ); // increment to the next sprite image indexX++; // move our sprite across the screen destX += 3; window.requestAnimationFrame(drawNextSprite); } drawNextSprite(); // draw the first frame });
#game{ border: solid black 1px;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script><canvas id="game" height="80" width="80"></canvas>
Related Topics
CSS Positioning 70-30 with Inline-Block
Rounded Bottom Div Where Curve Angle Is Not Responsive
Safari Position:Sticky Not Working in an Overflow:Auto Element
How to Make Border Radius in Popup Chrome Extension
CSS Styles Not Being Loaded in Ie8
Background Image Width Not 100% on Ipad
Why Is My CSS Class Style Not Applying to Specific <Li>'s
Change Background Colour as Page Scroll Without Jquery
How to Fix Safari Mix-Blend-Mode: Color-Dodge Bug
How to Set The Width and Height of a Textarea Using CSS
Horizontal Sharp Background Gradient with Specific Length of First Color
Ie 11 Ignores Min-Width When Using Flex Width
Can The CSS: Part Pseudo-Selector Be Used to Style Nested Web Components