Refactor Wagtail Images to srcset

This commit is contained in:
Lorenz Padberg 2024-04-30 13:55:32 +02:00
parent d69b32cba1
commit 0452389951
10 changed files with 65 additions and 90 deletions

View File

@ -1,6 +1,7 @@
<template>
<wagtail-image
:src="value.src"
:srcset="value.srcset"
alt=""
:original-height="value.height"
:original-width="value.width"
@ -17,7 +18,7 @@ export default {
components: { WagtailImage },
methods: {
openFullscreen() {
this.$store.dispatch('showFullscreenImage', this.value.url);
this.$store.dispatch('showFullscreenImage', this.value.src);
},
},
};

View File

@ -20,7 +20,7 @@ export default {
'-/resize/800/ 800w,'
);
}
return '';
return this.value.url;
},
},
methods: {

View File

@ -31,9 +31,12 @@
<wagtail-image
class="module__hero-image"
:src="module.heroImage.src"
:srcset="module.heroImage.srcset"
:original-width="module.heroImage.width"
:original-height="module.heroImage.height"
></wagtail-image>
<h5
class="module__hero-source"
v-if="module.heroSource"

View File

@ -6,6 +6,7 @@
<wagtail-image
class="module-teaser__image"
:src="heroImage.src"
:srcset="heroImage.srcset"
:original-height="heroImage.height"
:original-width="heroImage.width"
></wagtail-image>

View File

@ -1,9 +1,11 @@
<template>
<div class="wagtail-image">
<img
:src="modifiedUrl"
:src="props.src"
:srcset="props.srcset"
:alt="alt"
:class="['wagtail-image__image', { loaded: loaded }]"
:sizes="computedSizes"
loading="eager"
v-show="loaded"
ref="imgElement"
@ -16,15 +18,15 @@
></div>
</div>
</template>
<script setup>
import { ref, computed, onMounted } from 'vue';
import { ref, computed, onMounted, onBeforeUnmount } from 'vue';
const props = defineProps({
src: String,
alt: String(''),
originalWidth: Number,
originalHeight: Number,
srcset: String,
});
const imgElement = ref(null);
@ -32,19 +34,6 @@ const width = ref(0);
const height = ref(0);
const loaded = ref(false);
const modifiedUrl = computed(() => {
const density = isHighDensity() ? 2 : 1;
if (width.value) {
return props.src.replace('original', 'width-' + width.value * density);
}
if (height.value) {
return props.src.replace('original', 'height-' + height.value * density);
}
return props.src;
});
const updateDimensions = () => {
if (imgElement.value && imgElement.value.parentElement) {
const { clientWidth, clientHeight } = imgElement.value.parentElement;
@ -57,6 +46,23 @@ const handleLoad = () => {
loaded.value = true; // Set loaded to true when the image loads
};
const computedSizes = computed(() => {
// the default set of image sizes is [160px, 320px, 800px, 1600px]
let size;
if (width.value <= 300) {
size = '160px';
} else if (300 < width.value && width.value <= 600) {
size = '320px';
} else if (600 < width.value && width.value <= 1200) {
size = '800px';
} else {
size = '100vw';
}
console.log(size, width.value);
return size;
});
const placeholderStyle = computed(() => {
const styles = {
width: '100%',
@ -77,20 +83,15 @@ const placeholderStyle = computed(() => {
return styles;
});
const isHighDensity = () => {
return (
(window.matchMedia &&
(window.matchMedia(
'only screen and (min-resolution: 124dpi), only screen and (min-resolution: 1.3dppx), only screen and (min-resolution: 48.8dpcm)'
).matches ||
window.matchMedia(
'only screen and (-webkit-min-device-pixel-ratio: 1.3), only screen and (-o-min-device-pixel-ratio: 2.6/2), only screen and (min--moz-device-pixel-ratio: 1.3), only screen and (min-device-pixel-ratio: 1.3)'
).matches)) ||
(window.devicePixelRatio && window.devicePixelRatio > 1.3)
);
};
onMounted(() => {
console.log('src set', props.srcset);
updateDimensions();
window.addEventListener('resize', updateDimensions);
});
onMounted(updateDimensions);
onBeforeUnmount(() => {
window.removeEventListener('resize', updateDimensions);
});
</script>
<style scoped lang="scss">

View File

@ -5,4 +5,5 @@ fragment WagtailImageParts on WagtailImageNode {
width
height
title
srcset
}

View File

@ -6,7 +6,6 @@ from graphene.types import Scalar
from graphene_django.converter import convert_django_field
from graphql_relay import to_global_id
from wagtail.fields import StreamField
from wagtail.documents.models import Document
from wagtail.images.models import Image
from assignments.models import Assignment
@ -14,6 +13,7 @@ from basicknowledge.models import BasicKnowledge
from books.models import CustomDocument
from surveys.models import Survey
logger = logging.getLogger(__name__)
@ -43,17 +43,19 @@ def get_document_json(document_id):
def get_wagtail_image_dict(image_id: int) -> dict | None:
""""""
from books.schema.nodes import get_srcset, get_src
try:
image = Image.objects.get(id=image_id)
value = {
'value': image_id,
'id': image.id,
'src': generate_image_url(image, 'original'),
'src': get_src(image),
'alt': image.title,
'title': image.title,
'width': image.width,
'height': image.height,
'srcset': get_srcset(image),
}
return value
except Image.DoesNotExist:

View File

@ -7,8 +7,20 @@ from wagtail.images.models import Image
from wagtail.images.views.serve import generate_image_url
class WagtailImageNode(DjangoObjectType):
def get_srcset(image: Image) -> str:
return (
f"{generate_image_url(image, 'width-160')} 160w, "
f"{generate_image_url(image, 'width-320')} 320w, "
f"{generate_image_url(image, 'width-800')} 1280w, "
f"{generate_image_url(image, 'width-1600')} 2560w"
)
def get_src(image: Image) -> str:
return generate_image_url(image, f'width-{min(3840, image.width)}')
class WagtailImageNode(DjangoObjectType):
class Meta:
model = Image
fields = [
@ -20,10 +32,13 @@ class WagtailImageNode(DjangoObjectType):
src = graphene.String()
alt = graphene.String()
srcset = graphene.String()
def resolve_src(self, info):
return generate_image_url(self, 'original')
return get_src(self)
def resolve_alt(self, info):
return ""
return self.title
def resolve_srcset(self, info):
return get_srcset(self)

View File

@ -6,8 +6,7 @@ from django.views.generic import RedirectView
from wagtail.admin import urls as wagtailadmin_urls
from wagtail import urls as wagtail_urls
from wagtail.documents import urls as wagtaildocs_urls
#from wagtail.images import urls as wagtailimages_urls
from core.views import CustomImageServeView
from wagtail.images.views.serve import ServeView
from wagtailautocomplete.urls.admin import urlpatterns as autocomplete_admin_urls
@ -19,14 +18,13 @@ urlpatterns = [
re_path(r"^guru/", admin.site.urls),
re_path(r"^statistics/", include("statistics.urls", namespace="statistics")),
# wagtail
re_path(r'^api/images/([^/]*)/(\d*)/([^/]*)/[^/]*$', CustomImageServeView.as_view(action='redirect'), name='wagtailimages_serve'),
re_path(r'^api/images/([^/]*)/(\d*)/([^/]*)/[^/]*$', ServeView.as_view(action='redirect'), name='wagtailimages_serve'),
re_path(r"^cms/autocomplete/", include(autocomplete_admin_urls)),
re_path(r"^cms/pages/(\d+)/$", override_wagtailadmin_explore_default_ordering),
re_path(r"^cms/", include(wagtailadmin_urls)),
re_path(r"^documents/", include(wagtaildocs_urls)),
#re_path(r"^images/", include(wagtailimages_urls)),
# graphql backend
re_path(r"^api/", include("api.urls", namespace="api")),

View File

@ -1,29 +1,15 @@
import imghdr
from wsgiref.util import FileWrapper
import requests
from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin
from django.core.exceptions import PermissionDenied
from django.http import HttpResponse, StreamingHttpResponse
from django.http import HttpResponse
from django.http.request import HttpRequest
from django.http.response import HttpResponseRedirect
from django.shortcuts import get_object_or_404
from django.shortcuts import render
from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_control
from django.views.decorators.csrf import ensure_csrf_cookie
from graphene_django.views import GraphQLView
from graphql import get_operation_ast, parse
from sentry_sdk.api import start_transaction
from wagtail.admin.views.pages import listing
from wagtail.images import get_image_model
from wagtail.images.exceptions import InvalidFilterSpecError
from wagtail.images.models import Image
from wagtail.images.models import SourceImageIOError
from wagtail.images.utils import verify_signature
from wagtail.images.views.serve import ServeView
from core.logger import get_logger
@ -85,36 +71,3 @@ def override_wagtailadmin_explore_default_ordering(request, parent_page_id):
return listing.IndexView.as_view()(request, parent_page_id)
class CustomImageServeView(ServeView):
''' Taken from wagtail.images.views.serve.ServeView the only change is that we use original for the filter_spec_for_signature'''
model = get_image_model()
action = "serve"
key = None
@method_decorator(cache_control(max_age=3600, public=True))
def get(self, request, signature, image_id, filter_spec, filename=None):
# This variable is the only change to the wagtail implementation
filter_spec_for_signature = 'original'
if not verify_signature(
signature.encode(), image_id, filter_spec_for_signature, key=self.key
):
raise PermissionDenied
image = get_object_or_404(self.model, id=image_id)
# Get/generate the rendition
try:
rendition = image.get_rendition(filter_spec)
except SourceImageIOError:
return HttpResponse(
"Source image file not found", content_type="text/plain", status=410
)
except InvalidFilterSpecError:
return HttpResponse(
"Invalid filter spec: " + filter_spec,
content_type="text/plain",
status=400,
)
return getattr(self, self.action)(rendition)