|
try: |
|
import easy_thumbnails.files |
|
easy = True |
|
except ImportError: |
|
import sorl.thumbnail |
|
easy = False |
|
|
|
try: |
|
from jinja2 import Markup |
|
except ImportError: |
|
def Markup(s): |
|
return s |
|
|
|
from django.conf import settings |
|
|
|
|
|
REFERENCE_WIDTH = getattr(settings, 'SRCSET_REFERENCE_WIDTH', 1440) |
|
DESKTOP_MAX = getattr(settings, 'SRCSET_DESKTOP_MAX', 2000) |
|
MOBILE_MAX = getattr(settings, 'SRCSET_MOBILE_MAX', 767) |
|
MOBILE_MIN = getattr(settings, 'SRCSET_MOBILE_MIN', 375) |
|
|
|
|
|
def thumbnail(image, size, crop): |
|
if easy: |
|
thumbnailer = easy_thumbnails.files.get_thumbnailer(image) |
|
return thumbnailer.get_thumbnail({ |
|
'size': size, |
|
'crop': crop, |
|
}) |
|
else: |
|
return sorl.thumbnail.get_thumbnail(image, 'x'.join(size), crop) |
|
|
|
|
|
def generate_srcset(image, size, crop=None, retina=2): |
|
"""Generate srcset and sizes properties for an <img>. image should be |
|
a django image object, i.e. an instance of models.ImageField. |
|
size should be a (W, H) tuple or a WxH string. Returns srcset and sizes |
|
attributes for an <img> tag, wrapped in jinja2 Markup if available. |
|
Assumes the following: |
|
- the width component of size equals the img element's pixel width at |
|
browser size REFERENCE_WIDTH |
|
- between MOBILE_MAX and REFERENCE_WIDTH, the img resizes proportionally |
|
- below MOBILE_MAX, the img is 100% of the browser width |
|
- if image is not cropped, the thumbnail will be constrained by width, |
|
not height |
|
""" |
|
|
|
if not image: |
|
return '' |
|
|
|
if isinstance(size, str): |
|
size = tuple(map(int, size.split('x'))) |
|
|
|
w, h = size |
|
|
|
# standard size |
|
size_pairs = [(w, h)] |
|
|
|
# add retina thumbnail to srcset, but only if the source image is big |
|
# enough to warrant it |
|
if retina: |
|
retina_size = tuple(map(int, (w * retina, h * retina))) |
|
w_bigger = image.width > retina_size[0] |
|
h_bigger = image.height > retina_size[1] |
|
if w_bigger and h_bigger or not crop and (w_bigger or h_bigger): |
|
size_pairs.append(retina_size) |
|
|
|
# add mobile size if the original image is bigger, and if it's different |
|
# enough to be worth it |
|
mobile_w = MOBILE_MIN * 2 |
|
if w > mobile_w * 1.2: |
|
size_pairs.append((mobile_w, int(mobile_w * h / w))) |
|
|
|
# no srcset required if there's only one size |
|
if len(size_pairs) <= 1: |
|
return '' |
|
|
|
imgs = (thumbnail(image, s, crop=crop) for s in size_pairs) |
|
srcset = ', '.join('%s %sw' % (img.url, img.width) for img in imgs) |
|
|
|
# generate sizes - 100% below MOBILE_MAX, a pixel width above DESKTOP_MAX, |
|
# and a percentage in between |
|
# TODO account for actual resized image discrepancies here |
|
size_data = [ |
|
# min, max, image size |
|
(None, MOBILE_MAX, '100vw'), |
|
(MOBILE_MAX + 1, DESKTOP_MAX, |
|
'%svw' % (round(float(w) / REFERENCE_WIDTH * 100, 2))), |
|
(DESKTOP_MAX + 1, None, '%spx' % (w * DESKTOP_MAX / REFERENCE_WIDTH)) |
|
] |
|
|
|
def make_size(data): |
|
min_px, max_px, size = data |
|
mq_list = [] |
|
if min_px: |
|
mq_list.append('min-width: %spx' % min_px) |
|
if max_px: |
|
mq_list.append('max-width: %spx' % max_px) |
|
return '(%s) %s' % (') and ('.join(mq_list), size) |
|
|
|
sizes = ', '.join(map(make_size, size_data)) |
|
|
|
return Markup('srcset="%s" sizes="%s"' % (srcset, sizes)) |
please explain more, where should I put this code. Usage part is unclear