Skip to content

Commit

Permalink
Merge pull request #52 from sitegeist/feature/lazyloadPlaceholder
Browse files Browse the repository at this point in the history
[FEATURE] Placeholder image for lazyloading
  • Loading branch information
s2b authored Jul 9, 2019
2 parents 3fd0a43 + 62a3288 commit 319abb5
Show file tree
Hide file tree
Showing 15 changed files with 403 additions and 18 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
.Build
composer.lock
/Documentation-GENERATED-temp
8 changes: 7 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@ php:
- '7.0'
- '7.1'
- '7.2'
before_script: composer install
- '7.3'
cache:
directories:
- $HOME/.composer/cache/files
before_script:
- phpenv config-rm xdebug.ini
- composer install --prefer-dist
script:
- composer lint
- composer test
96 changes: 93 additions & 3 deletions Classes/Utility/ResponsiveImagesUtility.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ public function __construct(ImageService $imageService)
* @param bool $absoluteUri
* @param bool $lazyload
* @param array|string $ignoreFileExtensions
* @param int $placeholderSize
* @param bool $placeholderInline
*
* @return TagBuilder
*/
Expand All @@ -67,7 +69,9 @@ public function createImageTagWithSrcset(
bool $picturefillMarkup = true,
bool $absoluteUri = false,
bool $lazyload = false,
$ignoreFileExtensions = 'svg'
$ignoreFileExtensions = 'svg',
int $placeholderSize = 0,
bool $placeholderInline = false
): TagBuilder {
$tag = $tag ?: GeneralUtility::makeInstance(TagBuilder::class, 'img');

Expand Down Expand Up @@ -96,6 +100,17 @@ public function createImageTagWithSrcset(
$tag->addAttribute($attributePrefix . 'src', $fallbackImageUri);
}

// Create placeholder image for lazyloading
if ($lazyload && $placeholderSize) {
$tag->addAttribute('src', $this->generatePlaceholderImage(
$originalImage,
$placeholderSize,
$cropArea,
$placeholderInline,
$absoluteUri
));
}

// Generate different image sizes for srcset attribute
$srcsetImages = $this->generateSrcsetImages($originalImage, $referenceWidth, $srcset, $cropArea, $absoluteUri);
$srcsetMode = substr(key($srcsetImages), -1); // x or w
Expand Down Expand Up @@ -136,6 +151,8 @@ public function createImageTagWithSrcset(
* @param bool $absoluteUri
* @param bool $lazyload
* @param array|string $ignoreFileExtensions
* @param int $placeholderSize
* @param bool $placeholderInline
*
* @return TagBuilder
*/
Expand All @@ -150,7 +167,9 @@ public function createPictureTag(
bool $picturefillMarkup = true,
bool $absoluteUri = false,
bool $lazyload = false,
$ignoreFileExtensions = 'svg'
$ignoreFileExtensions = 'svg',
int $placeholderSize = 0,
bool $placeholderInline = false
): TagBuilder {
$tag = $tag ?: GeneralUtility::makeInstance(TagBuilder::class, 'picture');
$fallbackTag = $fallbackTag ?: GeneralUtility::makeInstance(TagBuilder::class, 'img');
Expand Down Expand Up @@ -212,6 +231,17 @@ public function createPictureTag(
}
}

// Create placeholder image for lazyloading
if ($lazyload && $placeholderSize) {
$fallbackTag->addAttribute('src', $this->generatePlaceholderImage(
$originalImage,
$placeholderSize,
$cropArea,
$placeholderInline,
$absoluteUri
));
}

// Provide image width to be consistent with TYPO3 core behavior
$fallbackTag->addAttribute('width', $referenceWidth);

Expand Down Expand Up @@ -298,6 +328,8 @@ public function createPictureSourceTag(
* @param Area $focusArea
* @param bool $absoluteUri
* @param bool $lazyload
* @param int $placeholderSize
* @param bool $placeholderInline
*
* @return TagBuilder
*/
Expand All @@ -307,7 +339,9 @@ public function createSimpleImageTag(
TagBuilder $tag = null,
Area $focusArea = null,
bool $absoluteUri = false,
bool $lazyload = false
bool $lazyload = false,
int $placeholderSize = 0,
bool $placeholderInline = false
): TagBuilder {
$tag = $tag ?: GeneralUtility::makeInstance(TagBuilder::class, 'img');
$fallbackImage = ($fallbackImage) ?: $originalImage;
Expand All @@ -318,6 +352,17 @@ public function createSimpleImageTag(
// Set image source
$tag->addAttribute($attributePrefix . 'src', $this->imageService->getImageUri($originalImage, $absoluteUri));

// Create placeholder image for lazyloading
if ($lazyload && $placeholderSize) {
$tag->addAttribute('src', $this->generatePlaceholderImage(
$originalImage,
$placeholderSize,
null,
$placeholderInline,
$absoluteUri
));
}

// Set image proportions
$tag->addAttribute('width', $fallbackImage->getProperty('width'));
$tag->addAttribute('height', $fallbackImage->getProperty('height'));
Expand Down Expand Up @@ -436,6 +481,51 @@ public function generateSrcsetImages(
return $images;
}

/**
* Generates a tiny placeholder image for lazyloading
*
* @param FileInterface $image
* @param integer $width
* @param Area $cropArea
* @param boolean $inline
* @param boolean $absoluteUri
*
* @return string
*/
public function generatePlaceholderImage(
FileInterface $image,
int $width = 20,
Area $cropArea = null,
bool $inline = false,
bool $absoluteUri = false
): string {
$cropArea = $cropArea ?: Area::createEmpty();

$processingInstructions = [
'width' => $width,
'crop' => $cropArea->isEmpty() ? null : $cropArea->makeAbsoluteBasedOnFile($image),
];
$processedImage = $this->imageService->applyProcessingInstructions($image, $processingInstructions);

if ($inline) {
return $this->generateDataUri($processedImage);
} else {
return $this->imageService->getImageUri($processedImage, $absoluteUri);
}
}

/**
* Generates a data URI for the specified image file
*
* @param FileInterface $image
*
* @return string
*/
public function generateDataUri(FileInterface $image): string
{
return 'data:' . $image->getMimeType() . ';base64,' . base64_encode($image->getContents());
}

/**
* Generates the content for a srcset attribute from an array of image urls
*
Expand Down
10 changes: 8 additions & 2 deletions Classes/ViewHelpers/ImageViewHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ public function initializeArguments()
$this->registerArgument('breakpoints', 'array', 'Image breakpoints from responsive design.', false);
$this->registerArgument('picturefill', 'bool', 'Use rendering suggested by picturefill.js', false, true);
$this->registerArgument('lazyload', 'bool', 'Generate markup that supports lazyloading', false, false);
$this->registerArgument('placeholderSize', 'int', 'Size of the placeholder image for lazyloading (0 = disabled)', false, 0);
$this->registerArgument('placeholderInline', 'bool', 'Embed placeholder image for lazyloading inline as data uri', false, false);
$this->registerArgument(
'ignoreFileExtensions',
'mixed',
Expand Down Expand Up @@ -117,7 +119,9 @@ public function render()
$this->arguments['picturefill'],
$this->arguments['absolute'],
$this->arguments['lazyload'],
$this->arguments['ignoreFileExtensions']
$this->arguments['ignoreFileExtensions'],
$this->arguments['placeholderSize'],
$this->arguments['placeholderInline']
);
} else {
// Generate img tag with srcset
Expand All @@ -132,7 +136,9 @@ public function render()
$this->arguments['picturefill'],
$this->arguments['absolute'],
$this->arguments['lazyload'],
$this->arguments['ignoreFileExtensions']
$this->arguments['ignoreFileExtensions'],
$this->arguments['placeholderSize'],
$this->arguments['placeholderInline']
);
}
} catch (ResourceDoesNotExistException $e) {
Expand Down
10 changes: 8 additions & 2 deletions Classes/ViewHelpers/MediaViewHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ public function initializeArguments()
$this->registerArgument('breakpoints', 'array', 'Image breakpoints from responsive design.', false);
$this->registerArgument('picturefill', 'bool', 'Use rendering suggested by picturefill.js', false, true);
$this->registerArgument('lazyload', 'bool', 'Generate markup that supports lazyloading', false, false);
$this->registerArgument('placeholderSize', 'int', 'Size of the placeholder image for lazyloading (0 = disabled)', false, 0);
$this->registerArgument('placeholderInline', 'bool', 'Embed placeholder image for lazyloading inline as data uri', false, false);
$this->registerArgument(
'ignoreFileExtensions',
'mixed',
Expand Down Expand Up @@ -102,7 +104,9 @@ protected function renderPicture(FileInterface $image, $width, $height)
$this->arguments['picturefill'],
false,
$this->arguments['lazyload'],
$this->arguments['ignoreFileExtensions']
$this->arguments['ignoreFileExtensions'],
$this->arguments['placeholderSize'],
$this->arguments['placeholderInline']
);

return $this->tag->render();
Expand Down Expand Up @@ -142,7 +146,9 @@ protected function renderImageSrcset(FileInterface $image, $width, $height)
$this->arguments['picturefill'],
false,
$this->arguments['lazyload'],
$this->arguments['ignoreFileExtensions']
$this->arguments['ignoreFileExtensions'],
$this->arguments['placeholderSize'],
$this->arguments['placeholderInline']
);

return $this->tag->render();
Expand Down
6 changes: 6 additions & 0 deletions Configuration/TypoScript/Base/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,10 @@ tx_smsresponsiveimages {

# cat=content/cTextmedia/b83; type=boolean; label= Generate image markup that supports lazyloading
lazyload = 0

# cat=content/cTextmedia/b84; type=integer; label= Size of placeholder image for lazyloading (0 = disabled)
placeholder.size = 0

# cat=content/cTextmedia/b85; type=boolean; label= Embed placeholder image for lazyloading inline as base64
placeholder.inline = 0
}
4 changes: 4 additions & 0 deletions Configuration/TypoScript/Base/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ lib.contentElement {
# Add responsive image settings to all content elements
settings.tx_smsresponsiveimages {
lazyload = {$tx_smsresponsiveimages.lazyload}
placeholder {
size = {$tx_smsresponsiveimages.placeholder.size}
inline = {$tx_smsresponsiveimages.placeholder.inline}
}
srcset = {$tx_smsresponsiveimages.srcset}
sizes = {$tx_smsresponsiveimages.sizes}
breakpoints {
Expand Down
49 changes: 49 additions & 0 deletions Documentation/DeveloperManual/Index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,55 @@ Examples
<sms:media file="{image}" srcset="400, 600" lazyload="true" />


placeholderSize
^^^^^^^^^^^^^^^
:aspect:`Variable type`
Integer

:aspect:`Description`
The size (= width) of the placeholder image that will be shown until lazyloading is finished.
If set to 0, placeholder images will be disabled and the ``src`` attribute will be skipped.

:aspect:`Default value`
0

:aspect:`Mandatory`
No

Examples
--------

::

<sms:image image="{image}" srcset="400, 600" lazyload="true" placeholderSize="20" />

<sms:media file="{image}" srcset="400, 600" lazyload="true" placeholderSize="20" />


placeholderInline
^^^^^^^^^^^^^^^^^
:aspect:`Variable type`
Boolean

:aspect:`Description`
If set to TRUE, the placeholder image for lazyloading will be inserted inline as base64 encoded uri.

:aspect:`Default value`
FALSE

:aspect:`Mandatory`
No

Examples
--------

::

<sms:image image="{image}" srcset="400, 600" lazyload="true" placeholderSize="20" placeholderInline="1" />

<sms:media file="{image}" srcset="400, 600" lazyload="true" placeholderSize="20" placeholderInline="1" />


ignoreFileExtensions
^^^^^^^^^^^^^^^^^^^^
:aspect:`Variable type`
Expand Down
2 changes: 1 addition & 1 deletion Documentation/Settings.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ conf.py:
copyright: 2017
project: SMS Responsive Images
version: 1.0
release: 1.2.0
release: 1.3.0
latex_documents:
- - Index
- sms_responsive_images.tex
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
title="{file.title}"
srcset="{settings.tx_smsresponsiveimages.srcset}"
lazyload="{settings.tx_smsresponsiveimages.lazyload}"
placeholderSize="{settings.tx_smsresponsiveimages.placeholder.size}"
placeholderInline="{settings.tx_smsresponsiveimages.placeholder.inline}"
sizes="{settings.tx_smsresponsiveimages.sizes}"
breakpoints="{settings.tx_smsresponsiveimages.breakpoints}"
picturefill="{settings.tx_smsresponsiveimages.picturefill}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ protected function mockImageService()
// Use file name and extension from original image
$instructions['name'] = $file->getProperty('name');
$instructions['extension'] = $file->getProperty('extension');
$instructions['mimeType'] = $file->getProperty('mimeType');

return $test->mockFileObject($instructions);
}));
Expand All @@ -58,14 +59,24 @@ protected function mockFileObject($properties)

$fileMock = $this->getMockBuilder(FileReference::class)
->disableOriginalConstructor()
->setMethods(['getProperty'])
->setMethods(['getProperty', 'getMimeType', 'getContents'])
->getMock();

$fileMock
->method('getProperty')
->will($this->returnCallback(function ($property) use ($properties) {
return $properties[$property];
}));
$fileMock
->method('getMimeType')
->will($this->returnCallback(function () use ($properties) {
return $properties['mimeType'];
}));
$fileMock
->method('getContents')
->will($this->returnCallback(function () use ($properties) {
return 'das-ist-der-dateiinhalt';
}));

return $fileMock;
}
Expand Down
Loading

0 comments on commit 319abb5

Please sign in to comment.