Skip to content

Commit

Permalink
[FEATURE] Add srcPrefix (data-*) to support edge cases when loading="…
Browse files Browse the repository at this point in the history
…lazy"is not enough.
  • Loading branch information
kszymukowicz committed Oct 29, 2024
1 parent 2622749 commit 2331ffc
Show file tree
Hide file tree
Showing 8 changed files with 113 additions and 17 deletions.
7 changes: 7 additions & 0 deletions Classes/Domain/Model/PictureConfiguration.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class PictureConfiguration
protected array $breakpoints = [];
protected array $sources = [];
protected bool $addSources = false;
protected bool $addSrcPrefix = false;
protected bool $addLazyLoading = false;
protected string $lazyLoading = '';
protected array $arguments;
Expand All @@ -35,6 +36,7 @@ public function __construct(array $arguments, array $typoScriptSettings, FileInt
{
$this->arguments = $arguments;
$fileExtension = $arguments['fileExtension'] ?? $image->getExtension();
$this->addSrcPrefix = $arguments['srcPrefix'] ?? false;
if ($image->getExtension() !== 'svg') {
$this->addWebp = (bool)($fileExtension === 'webp' ? false : ($arguments['addWebp'] ?? $typoScriptSettings['addWebp'] ?? false));
$this->onlyWebp = (bool)($fileExtension === 'webp' ? false : ($arguments['onlyWebp'] ?? $typoScriptSettings['onlyWebp'] ?? false));
Expand Down Expand Up @@ -98,6 +100,11 @@ public function getSourceConfiguration(): array
return $configuration;
}

public function srcPrefixShouldBeAdded(): bool
{
return $this->addSrcPrefix;
}

public function lazyLoadingShouldBeAdded(): bool
{
return $this->addLazyLoading;
Expand Down
30 changes: 22 additions & 8 deletions Classes/ViewHelpers/ImageViewHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,13 @@ public function initializeArguments(): void
'array',
'Array for rendering of multiple images.'
);

$this->registerArgument(
'srcPrefix',
'bool',
'If true then adds data-* prefix to all src and srcset'
);

}

/**
Expand Down Expand Up @@ -261,6 +268,7 @@ protected function buildSingleTag(string $tagName, array $configuration, FileInt
// set the default processed image (we might need the width and height of this image later on) and generate a single image uri as the src fallback
$processedImage = $this->applyProcessingInstructions($processingInstructions, $image);
$imageUri = $this->imageService->getImageUri($processedImage, $this->arguments['absolute']);
$srcPrefix = $this->getSrcPrefix();

switch ($tagName) {
case 'img':
Expand All @@ -273,9 +281,9 @@ protected function buildSingleTag(string $tagName, array $configuration, FileInt
}
}
if ($srcsetValue !== '') {
$tag->addAttribute('srcset', $srcsetValue);
$tag->addAttribute($srcPrefix . 'srcset', $srcsetValue);
}
$tag->addAttribute('src', $imageUri);
$tag->addAttribute($srcPrefix . 'src', $imageUri);
if (!empty($configuration['sizes'])) {
$tag->addAttribute('sizes', $configuration['sizes']);
}
Expand Down Expand Up @@ -303,9 +311,9 @@ protected function buildSingleTag(string $tagName, array $configuration, FileInt

// Add content of src attribute to srcset attribute as the source element has no src attribute.
if ($srcsetValue !== '') {
$tag->addAttribute('srcset', $srcsetValue);
$tag->addAttribute($srcPrefix . 'srcset', $srcsetValue);
} else {
$tag->addAttribute('srcset', $imageUri);
$tag->addAttribute($srcPrefix . 'srcset', $imageUri);
}

// Add attributes.
Expand Down Expand Up @@ -373,12 +381,13 @@ protected function addRetina(array $processingInstructions, TagBuilder $tag, Fil
// Process regular image.
$processedImageRegular = $this->applyProcessingInstructions($processingInstructions, $image);
$imageUriRegular = $this->imageService->getImageUri($processedImageRegular, $this->arguments['absolute']);
$srcPrefix = $this->getSrcPrefix();

// Process additional retina images. Tag value can be gathered for source tags from srcset value as there it
// was to be set already because adding retina is not mandatory.
if ($tag->hasAttribute('srcset')) {
$tagValue = htmlspecialchars_decode($tag->getAttribute('srcset') ?? '');
$tag->removeAttribute('srcset');
if ($tag->hasAttribute($srcPrefix . 'srcset')) {
$tagValue = htmlspecialchars_decode($tag->getAttribute($srcPrefix . 'srcset') ?? '');
$tag->removeAttribute($srcPrefix . 'srcset');
} else {
$tagValue = $imageUriRegular;
}
Expand All @@ -405,7 +414,7 @@ protected function addRetina(array $processingInstructions, TagBuilder $tag, Fil
$tagValue .= ', ' . $imageUriRetina . ' ' . $retinaString;
}

$tag->addAttribute('srcset', $tagValue);
$tag->addAttribute($srcPrefix . 'srcset', $tagValue);
}

/**
Expand Down Expand Up @@ -536,4 +545,9 @@ protected function applyProcessingInstructions(array $processingInstructions, Fi

return $this->imageService->applyProcessingInstructions($image, $processingInstructions);
}

public function getSrcPrefix(): string
{
return $this->pictureConfiguration->srcPrefixShouldBeAdded() ? 'data-' : '';
}
}
23 changes: 14 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,16 @@ Use a proper configured Fluid template adding the namespace when using this View

See `EXT:picture/Configuration/TypoScript/setup.typoscript` for possible configuration options (key: `plugin.tx_picture`):

| TypoScript Configuration option | Description |
|---------------------------------|-------------|
| addWebp | Add webp alternative image files as sources. <br>_default: 0_ |
| onlyWebp | Enable only images in webp format and for all size variants. <br>_default: 0_ |
| useRetina | Add retina (2x) version of all images as sizes variants. <br>_default: 0_ |
| lossless | Enable lossless compression for webp images. <br>_default: 0_ |
| retina | Use custom or multiple multipliers for calculating retina image variants. <br>_default: <br>retina.2 = 2x<br>Only works in combination with `useRetina = 1` |
| breakpoints | Use named breakpoints for easier markup of different image sizes for one picture element.<br>_default: empty_. |
| lazyLoading | Use the `loading` attribute with images. See [Browser Native Lazy Loading by Default](https://b13.com/blog/lazy-loading-just-got-lazier-in-typo3-v10)<br>_default: {$types.content.image.lazyLoading}_ |
| TypoScript Configuration option | Description |
|---------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| addWebp | Add webp alternative image files as sources. <br>_default: 0_ |
| onlyWebp | Enable only images in webp format and for all size variants. <br>_default: 0_ |
| srcPrefix | Enable data-* prefix to all src and srcset. <br>_default: 0_ |
| useRetina | Add retina (2x) version of all images as sizes variants. <br>_default: 0_ |
| lossless | Enable lossless compression for webp images. <br>_default: 0_ |
| retina | Use custom or multiple multipliers for calculating retina image variants. <br>_default: <br>retina.2 = 2x<br>Only works in combination with `useRetina = 1` |
| breakpoints | Use named breakpoints for easier markup of different image sizes for one picture element.<br>_default: empty_. |
| lazyLoading | Use the `loading` attribute with images. See [Browser Native Lazy Loading by Default](https://b13.com/blog/lazy-loading-just-got-lazier-in-typo3-v10)<br>_default: {$types.content.image.lazyLoading}_ |

## Attributes

Expand All @@ -67,6 +68,10 @@ This attribute is ignored if `onlyWebp` option is active.
Enable only images in webp format and for all size variants.
Enabling this option disables `addWebp` setting.

### srcPrefix
Enable `data-*` prefix to all `src` and `srcset` allowing to use all king do JS lazy load images libraries. Can be used
in cases when standard `loading="lazy"` is not enough.

### lossless
Enable lossless compression for webp images. If you find your webp images lacking in quality compared to jpg/png images, enable
this option to overwrite default settings for ImageMagick/GraphicsMagick.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<html
xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers"
xmlns:i="http://typo3.org/ns/B13/Picture/ViewHelpers"
data-namespace-typo3-fluid="true"
>
<f:spaceless>
<i:image src="EXT:picture/Resources/Public/Test/Picture.png" width="400" useRetina="1" addWebp="1" srcPrefix="1" alt="Testimage 400px width with retina and addWebp and srcPrefix" />
</f:spaceless>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<html
xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers"
xmlns:i="http://typo3.org/ns/B13/Picture/ViewHelpers"
data-namespace-typo3-fluid="true"
>
<f:spaceless>
<i:image src="EXT:picture/Resources/Public/Test/Picture.png" width="400" srcPrefix="1" alt="Testimage 400px width and srcPrefix" />
</f:spaceless>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
"pages"
,"uid","pid","title","slug"
,1,0,"root","/"
"sys_template"
,"uid","pid","root","constants","config"
,1,1,1,styles.content.image.lazyLoading=lazy,"@import 'EXT:picture/Configuration/TypoScript/test.typoscript'
page = PAGE
page.config.disableAllHeaderCode = 1
page.10 = FLUIDTEMPLATE
page.10.templateRootPaths.10 = EXT:picture/Tests/Functional/Frontend/Fixtures/Templates
page.10.templateName = SimpleImageWithRetinaAndWebpAndSrcPrefixOption.html
config.absRefPrefix = /
"
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
"pages"
,"uid","pid","title","slug"
,1,0,"root","/"
"sys_template"
,"uid","pid","root","constants","config"
,1,1,1,styles.content.image.lazyLoading=lazy,"@import 'EXT:picture/Configuration/TypoScript/test.typoscript'
page = PAGE
page.config.disableAllHeaderCode = 1
page.10 = FLUIDTEMPLATE
page.10.templateRootPaths.10 = EXT:picture/Tests/Functional/Frontend/Fixtures/Templates
page.10.templateName = SimpleImageWithSrcPrefix.html
config.absRefPrefix = /
"
28 changes: 28 additions & 0 deletions Tests/Functional/Frontend/TagRenderingTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,22 @@ public function simpleImageWithOnlyWebp(): void
self::assertStringContainsString($this->anonymouseProcessdImage($expected), $this->anonymouseProcessdImage($body));
}

/**
* @test
*/
public function simpleImageWithRetinaAndWebpAndSrcPrefixOption(): void
{
$this->importCSVDataSet(__DIR__ . '/Fixtures/simple_image_with_retina_and_webp_and_src_prefix_option.csv');
$response = $this->executeFrontendSubRequest(new InternalRequest('http://localhost/'));
$body = (string)$response->getBody();
$expected = '<picture>
<source type="image/webp" data-srcset="/typo3temp/assets/_processed_/a/2/csm_Picture_cfb567934c.webp, /typo3temp/assets/_processed_/a/2/csm_Picture_089357224d.webp 2x, /typo3temp/assets/_processed_/a/2/csm_Picture_d356d2dde1.webp 3x" />
<img alt="Testimage 400px width with retina and addWebp and srcPrefix" data-src="/typo3temp/assets/_processed_/a/2/csm_Picture_23f7889ff5.png" width="400" height="200" loading="lazy" data-srcset="/typo3temp/assets/_processed_/a/2/csm_Picture_23f7889ff5.png, /typo3temp/assets/_processed_/a/2/csm_Picture_13dd378eeb.png 2x, /typo3temp/assets/_processed_/a/2/csm_Picture_3c8b5cfedf.png 3x" />
</picture>';
$expected = implode('', GeneralUtility::trimExplode("\n", $expected));
self::assertStringContainsString($this->anonymouseProcessdImage($expected), $this->anonymouseProcessdImage($body));
}

/**
* @test
*/
Expand All @@ -77,6 +93,18 @@ public function simpleImageWithRetinaOption(): void
self::assertStringContainsString($this->anonymouseProcessdImage($expected), $this->anonymouseProcessdImage($body));
}

/**
* @test
*/
public function simpleImageWithSrcPrefix(): void
{
$this->importCSVDataSet(__DIR__ . '/Fixtures/simple_image_with_src_prefix.csv');
$response = $this->executeFrontendSubRequest(new InternalRequest('http://localhost/'));
$body = (string)$response->getBody();
$expected = '<img alt="Testimage 400px width and srcPrefix" data-src="/typo3temp/assets/_processed_/a/2/csm_Picture_23f7889ff5.png" width="400" height="200" loading="lazy" />';
self::assertStringContainsString($this->anonymouseProcessdImage($expected), $this->anonymouseProcessdImage($body));
}

/**
* @test
*/
Expand Down

0 comments on commit 2331ffc

Please sign in to comment.