New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Retina sprites #19

Open
mmhd opened this Issue Aug 14, 2012 · 9 comments

Comments

Projects
None yet
9 participants
@mmhd

mmhd commented Aug 14, 2012

It would be amazing if we could add @2x to icons which are the Retina counterparts of their non-Retina versions. Doing would create a new rule in the same stylesheet but only for @2x versions. Some JavaScript would be needed to handle when the @2x version is used.

@joeylomanto

This comment has been minimized.

joeylomanto commented Nov 13, 2012

I agree. This would be great. Glue does something similar to what you're describing. http://glue.readthedocs.org/en/latest/ratios.html

@matsuodesign

This comment has been minimized.

matsuodesign commented Dec 19, 2012

I'm close to coming up with a solution by using sprite-factory's "Customizing the entire CSS output" method. I'm passing variables into a SASS mixin that then writes most of the necessary css/media query. I'm also using this as a general mixin for use in my stylesheets, so all the variables that'd come from sprite factory are optional and come after the $sprite: false declaration.

The only real issue I'm running into is trying to figure out a way to access the width and height of the final output image (necessary to set the correct background size on retina). I feel like I'm probably overlooking something simple... have tried using :cssw, :cssh. Any suggestions?

The code assumes you're starting with your highest resolution assets; I may try to integrate some sort of image processor after the sprite-factory call to create the second image scaled at 50%.

Again any suggestions on accessing the final output size, or code improvements would be greatly appreciated!
And if you're curious I'm using retina.js for images outside the stylesheet.

@mixin retina_url($path, $filetype, $repeat: no-repeat, $rwidth: auto, $rheight: auto, $sprite: false, $sprite-posx: 0, $sprite-posy: 0, $sprite-width: 0, $sprite-height: 0)
  display: inline-block
  background-image: image-url('#{$path}.#{$filetype}')
  background-repeat: $repeat
  width: $rwidth
  height: $rheight
  @if $sprite
    background-position-x: -($sprite-posx / 2)
    background-position-y: -($sprite-posy / 2)
  // RETINA MEDIA QUERY, MAY ALSO NEED -MOZ PREFIX
  @media all and (-webkit-min-device-pixel-ratio : 1.5)
    background-image: image-url('#{$path}@2x.#{$filetype}')
    background-repeat: $repeat
    @if $sprite
      background-position-x: -($sprite-posx)
      background-position-y: -($sprite-posy)
      background-size: ($sprite-width / 2) ($sprite-width / 2)
    @else
      background-size: $rwidth $rheight

Here's my custom css output:

    SpriteFactory.run!('app/assets/images/us_sprite', :nocomments => 'true', :output_style => 'app/assets/stylesheets/sprites/us_sprite.css.sass', :output_image => "app/assets/images/compiled/us_sprite@2x.png") do |images|
      #Writing SASS, mostly done with mixin retina_url()
      images.map do |image_name, image_data|
      ".#{image_name}\n\t@include retina_url(compiled/us_sprite, png, no-repeat, #{image_data[:width] / 2}px, #{image_data[:height] / 2}px, true, #{images[:cssx]}px, #{images[:cssy]}px)"
      end.join("\n")
    end
@ANTON072

This comment has been minimized.

ANTON072 commented Jun 1, 2013

Awesome!
It's great!!!!!!

@ryana

This comment has been minimized.

ryana commented May 2, 2014

Any thoughts on this? I'm surprised this thread has been stale for so long. I would have it would be taken care of, or a reason to not do it would have been discovered

@keithchu

This comment has been minimized.

keithchu commented May 3, 2014

@matsuodesign I'm using https://github.com/ai/rails-sass-images to grab the image width of the 2x sprite.

After that, it's just background-size: (image-width("compiled/us_sprite@2x.png") / 2) auto.

@keithchu

This comment has been minimized.

keithchu commented May 3, 2014

FYI, @matsuodesign's workaround is working for me.

Here's my configuration if it helps. I'm using https://github.com/ai/rails-sass-images to grab the width of the output 2x sprite. Also, the +respond-to($hidpi) is just a helper for targeting @media screen and (-webkit-max-device-pixel-ratio: 1.5), screen and (max-resolution: 1.5dppx).

Ruby:

SpriteFactory.run!('app/assets/images/sprites/2x',
                         :output_style => 'app/assets/stylesheets/_sprites-2x.sass.erb',
                         :output_image => 'app/assets/images/sprites/sprites@2x.png',
                         :selector => '.icon-2x-') do |images|
  images.map do |image_name, image_data|
    ".icon-2x-#{image_name}\n\t@include hidpi-sprite(" +
    "'sprites/sprites@2x.png', " +
    "#{image_data[:cssw]}px, " +
    "#{image_data[:cssh]}px, " +
    "#{image_data[:cssx]}px, " +
    "#{image_data[:cssy]}px)"
  end.join("\n")
end

Sass:

// _icons.sass
=icon($sprite-name: null, $image-name: null)
  +respond-to($hidpi)
    @extend .icon-2x-#{$sprite-name}_#{$image-name}
=hidpi-sprite($path, $width: 0, $height: 0, $x: auto, $y: auto)
  background-image: image-url('#{$path}')
  background-repeat: no-repeat
  background-position: ($x / -2) ($y / -2)
  background-size: (image-width("sprites/sprites@2x.png") / 2) auto
  height: $height / 2
  width: $width / 2

// usage:
.icon-facebook
  &:hover
    +icon("social", "facebook-hover")
@sethjeffery

This comment has been minimized.

sethjeffery commented Apr 21, 2016

Hi guys, I understand this thread is 2 years old but it's still open so in case others are searching like me, here is the method I am using that doesn't rely on rails-sass-images. It makes an assumption (seems to be correct) that the generated sprite file won't have extra padding around the sprites.

SpriteFactory.run!('app/assets/images/sprites/2x') do |images|

  # Find the max right-most point of any sprite in the list.
  # This must therefore be the image width.
  image_width  = images.values.map{|data| data[:x] + data[:width]}.max

  # The rest is standard issue, now that we know the background size.
  images.map do |name, data|
    <<-SCSS
      .icon-2x-#{name} {
        width: #{data[:width] / 2}px #{data[:height] / 2}px;
        background: image-url(sprites/sprites@2x.png) #{data[:x] / -2}px #{data[:y] / -2}px no-repeat;
        background-size: #{image_width / 2}px auto;
      }
    SCSS
  end.join("\n")
end

Updated 18th April 2017

@masterkain

This comment has been minimized.

masterkain commented Feb 12, 2017

hello, in the proposed solution I find that data[:w] is nil, both with chunkypng and rmagick. any solution to that?

@nburwell

This comment has been minimized.

nburwell commented Apr 14, 2017

@masterkain looks like the data is now data[:width] and data[:height]

@sethjeffery any chance you could update your example block of code with the updated keys from the images hash?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment