Class: DynamicImage::Picture

Inherits:
Object
  • Object
show all
Defined in:
lib/dynamic_image/picture.rb,
lib/dynamic_image/picture/format_policy.rb

Overview

DynamicImage Picture

Everything needed to render an image responsively: the candidate widths, the signed URLs behind them, and the fallback the img points at.

Sizing is expressed as an optional ratio instead of a size.

Examples:

picture = DynamicImage::Picture.new(self, image, ratio: "16:9")
picture.srcset # => "/images/… 420w, /images/… 590w, …"
picture.src    # => "/images/…/1200x675/…jpg"

See Also:

Defined Under Namespace

Classes: FormatPolicy

Constant Summary collapse

OPTIONS =

The options this class consumes. Anything else is passed on to the router.

i[ratio sizes breakpoints step fallback_width format uncropped].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(template, record_or_array, options = {}) ⇒ Picture

Any options supported by polymorphic_url are also accepted, and passed on to the router.

Parameters:

  • template (ActionView::Base)

    the view context, for routing

  • record_or_array (DynamicImage::Model, Array)

    the record, or an array of records for a nested route

  • options (Hash) (defaults to: {})

Options Hash (options):

  • :ratio (Numeric, Vector2d, String, nil)

    The aspect ratio to crop to, as a number, a vector, or a string like "16:9". Implies cropping. Omit for the image's own.

  • :sizes (String)

    The sizes attribute

  • :breakpoints (Range, Array<Integer>, Integer)

    The widths to offer, overriding DynamicImage#default_breakpoints

  • :step (Numeric)

    The step between breakpoints, overriding DynamicImage#breakpoint_step

  • :fallback_width (Integer)

    The width to ask for the fallback image, overriding DynamicImage#picture_fallback_width

  • :format (Symbol, Array<Symbol>)

    The format the candidates are rendered in. A symbol forces that format, an array is negotiated. Defaults to WebP, or the image's own format if it is animated.

  • :uncropped (Boolean)

    Size against the whole image, ignoring any pre-cropping. Defaults to whether the URL points at the uncropped action.



51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/dynamic_image/picture.rb', line 51

def initialize(template, record_or_array, options = {})
  options = options.symbolize_keys
  @template = template
  @record_or_array = Array(record_or_array)
  @ratio = DynamicImage::Ratio.parse(options[:ratio])
  @sizes = options[:sizes] || "100vw"
  @requested_format = options[:format]
  @fallback_width = fallback_width_from(options)
  @breakpoints = breakpoints_from(options)
  @url_options = options.except(*OPTIONS)
  @uncropped = options.fetch(:uncropped) { url_options[:action].to_s == "uncropped" }
end

Instance Attribute Details

#breakpointsDynamicImage::Breakpoints (readonly)

Returns the candidate widths.

Returns:



32
# File 'lib/dynamic_image/picture.rb', line 32

attr_reader :template, :record_or_array, :ratio, :sizes, :breakpoints, :fallback_width, :url_options

#fallback_widthInteger (readonly)

Returns the width asked for the fallback image.

Returns:

  • (Integer)

    the width asked for the fallback image



32
# File 'lib/dynamic_image/picture.rb', line 32

attr_reader :template, :record_or_array, :ratio, :sizes, :breakpoints, :fallback_width, :url_options

#ratioFloat? (readonly)

Returns:

  • (Float, nil)


32
# File 'lib/dynamic_image/picture.rb', line 32

attr_reader :template, :record_or_array, :ratio, :sizes, :breakpoints, :fallback_width, :url_options

#record_or_arrayObject (readonly)

Returns the value of attribute record_or_array.



32
33
34
# File 'lib/dynamic_image/picture.rb', line 32

def record_or_array
  @record_or_array
end

#sizesString (readonly)

Returns the sizes attribute.

Returns:

  • (String)

    the sizes attribute



32
# File 'lib/dynamic_image/picture.rb', line 32

attr_reader :template, :record_or_array, :ratio, :sizes, :breakpoints, :fallback_width, :url_options

#templateObject (readonly)

Returns the value of attribute template.



32
33
34
# File 'lib/dynamic_image/picture.rb', line 32

def template
  @template
end

#url_optionsObject (readonly)

Returns the value of attribute url_options.



32
33
34
# File 'lib/dynamic_image/picture.rb', line 32

def url_options
  @url_options
end

Instance Method Details

#available_widthInteger

The widest the image can be rendered at: its own width, or the width of the largest crop matching the ratio.

Returns:

  • (Integer)


74
75
76
# File 'lib/dynamic_image/picture.rb', line 74

def available_width
  @available_width ||= sizing.available_width(ratio)
end

#crop?Boolean

Returns true if the image is cropped, which it is whenever a ratio is given.

Returns:

  • (Boolean)


67
68
69
# File 'lib/dynamic_image/picture.rb', line 67

def crop?
  !ratio.nil?
end

#dimensionsVector2d

The size the fallback image is actually rendered at. Smaller than #fallback_size when the image is.

Returns:

  • (Vector2d)


139
140
141
# File 'lib/dynamic_image/picture.rb', line 139

def dimensions
  @dimensions ||= sizing.fit(fallback_size, crop: crop?).floor
end

#fallback_formatDynamicImage::Format

The format the fallback image is rendered in, negotiated from COMPATIBLE_FORMATS.



125
126
127
# File 'lib/dynamic_image/picture.rb', line 125

def fallback_format
  format_policy.fallback
end

#fallback_sizeString

The size asked for the fallback image, as a "{width}x{height}" string.

Returns:

  • (String)


132
133
134
# File 'lib/dynamic_image/picture.rb', line 132

def fallback_size
  @fallback_size ||= size_for(fallback_width)
end

#heightInteger

The height of the fallback image.

Returns:

  • (Integer)


160
161
162
# File 'lib/dynamic_image/picture.rb', line 160

def height
  dimensions.y.to_i
end

#sourcesArray<Hash>

The sources to render, as type and srcset pairs.

Empty when the candidates are in the same format as the fallback; #srcset goes on the img instead. That is what happens to an animated image, which keeps its own format.

Returns:

  • (Array<Hash>)


108
109
110
111
112
113
# File 'lib/dynamic_image/picture.rb', line 108

def sources
  candidates = srcset
  return [] if candidates.nil? || format == fallback_format

  [{ type:, srcset: candidates }]
end

#srcString

The URL for the fallback image.

Returns:

  • (String)


146
147
148
# File 'lib/dynamic_image/picture.rb', line 146

def src
  url_for(fallback_size, fallback_format)
end

#srcsetString?

The srcset attribute for the candidates, or nil if there are none. It belongs on the source when #sources has one, and on the img when it doesn't.

Returns:

  • (String, nil)


96
97
98
99
100
# File 'lib/dynamic_image/picture.rb', line 96

def srcset
  return if variants.empty?

  variants.map { |v| "#{v[:url]} #{v[:width]}w" }.join(", ")
end

#typeString

The content type the candidates are rendered as, for the type attribute.

Returns:

  • (String)


118
119
120
# File 'lib/dynamic_image/picture.rb', line 118

def type
  format.content_type
end

#variantsArray<Hash>

Every candidate, as the URL and the size it is actually rendered at.

Returns:

  • (Array<Hash>)


88
89
90
# File 'lib/dynamic_image/picture.rb', line 88

def variants
  @variants ||= widths.map { |width| variant(width) }
end

#widthInteger

The width of the fallback image.

Returns:

  • (Integer)


153
154
155
# File 'lib/dynamic_image/picture.rb', line 153

def width
  dimensions.x.to_i
end

#widthsArray<Integer>

The candidate widths, smallest first.

Returns:

  • (Array<Integer>)


81
82
83
# File 'lib/dynamic_image/picture.rb', line 81

def widths
  @widths ||= breakpoints.widths(available_width)
end