Last active
November 14, 2018 21:00
-
-
Save lorenzulrich/d9a680b7989b884011dd24357588f533 to your computer and use it in GitHub Desktop.
ResponsiveImageViewHelper and SrcSetViewHelper for Responsive Images using lazysizes/bgset
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<div class="lazyload" data-bgset="{v:srcSet(image: media.1, ratio: 1, maxWidth: 1920)}"> | |
Foo | |
</div> | |
{namespace v=Visol\Foobar\ViewHelpers} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# All sizes variants to be generated for an image up to the maximumWidth | |
config.responsiveImage.sizes = 16, 48, 96, 160, 320, 480, 640, 960, 1024, 1440, 1920, 2560, 3840, 5120 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<v:responsiveImage image="{article.media.0}" class="img-fluid" ratio="1" maxWidth="800"/> | |
{namespace v=Visol\Foobar\ViewHelpers} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Visol\Foobar\ViewHelpers; | |
/* * | |
* This script is part of the TYPO3 project - inspiring people to share! * | |
* * | |
* TYPO3 is free software; you can redistribute it and/or modify it under * | |
* the terms of the GNU General Public License version 2 as published by * | |
* the Free Software Foundation. * | |
* * | |
* This script is distributed in the hope that it will be useful, but * | |
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- * | |
* TABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General * | |
* Public License for more details. * | |
* */ | |
use TYPO3\CMS\Core\Resource\Exception\ResourceDoesNotExistException; | |
use TYPO3\CMS\Core\Utility\GeneralUtility; | |
/** | |
* Resizes a given image (if required) and renders the respective img tag | |
* | |
* = Examples = | |
* | |
* <code title="Default"> | |
* <f:image src="EXT:myext/Resources/Public/typo3_logo.png" alt="alt text" /> | |
* </code> | |
* <output> | |
* <img alt="alt text" src="typo3conf/ext/myext/Resources/Public/typo3_logo.png" width="396" height="375" /> | |
* or (in BE mode): | |
* <img alt="alt text" src="../typo3conf/ext/viewhelpertest/Resources/Public/typo3_logo.png" width="396" height="375" /> | |
* </output> | |
* | |
* <code title="Image Object"> | |
* <f:image image="{imageObject}" /> | |
* </code> | |
* <output> | |
* <img alt="alt set in image record" src="fileadmin/_processed_/323223424.png" width="396" height="375" /> | |
* </output> | |
* | |
* <code title="Inline notation"> | |
* {f:image(src: 'EXT:viewhelpertest/Resources/Public/typo3_logo.png', alt: 'alt text', minWidth: 30, maxWidth: 40)} | |
* </code> | |
* <output> | |
* <img alt="alt text" src="../typo3temp/assets/images/f13d79a526.png" width="40" height="38" /> | |
* (depending on your TYPO3s encryption key) | |
* </output> | |
* | |
* <code title="Other resource type (e.g. PDF)"> | |
* <f:image src="fileadmin/user_upload/example.pdf" alt="foo" /> | |
* </code> | |
* <output> | |
* If your graphics processing library is set up correctly then it will output a thumbnail of the first page of your PDF document. | |
* <img src="fileadmin/_processed_/1/2/csm_example_aabbcc112233.gif" width="200" height="284" alt="foo"> | |
* </output> | |
* | |
* <code title="Non-existent image"> | |
* <f:image src="NonExistingImage.png" alt="foo" /> | |
* </code> | |
* <output> | |
* Could not get image resource for "NonExistingImage.png". | |
* </output> | |
*/ | |
class ResponsiveImageViewHelper extends \TYPO3\CMS\Fluid\ViewHelpers\ImageViewHelper | |
{ | |
const RATIO_PATTERN = '/(\d):(\d)/'; | |
/** | |
* @var string | |
*/ | |
protected $tagName = 'img'; | |
/** | |
* @var \Visol\Foobar\Service\SrcSetService | |
*/ | |
protected $srcSetService; | |
/** | |
* @param \Visol\Foobar\Service\SrcSetService $srcSetService | |
*/ | |
public function injectSrcSetService(\Visol\Foobar\Service\SrcSetService $srcSetService) | |
{ | |
$this->srcSetService = $srcSetService; | |
} | |
/** | |
* Initialize arguments. | |
*/ | |
public function initializeArguments() | |
{ | |
$this->registerArgument('additionalAttributes', 'array', 'Additional tag attributes. They will be added directly to the resulting HTML tag.', false); | |
$this->registerArgument('data', 'array', 'Additional data-* attributes. They will each be added with a "data-" prefix.', false); | |
$this->registerUniversalTagAttributes(); | |
$this->registerTagAttribute('alt', 'string', 'Specifies an alternate text for an image', false); | |
$this->registerArgument( | |
'src', | |
'string', | |
'a path to a file, a combined FAL identifier or an uid (int). | |
If $treatIdAsReference is set, the integer is considered the uid of the sys_file_reference record. | |
If you already got a FAL object, consider using the $image parameter instead' | |
); | |
$this->registerArgument('treatIdAsReference', 'bool', 'given src argument is a sys_file_reference record'); | |
$this->registerArgument('image', 'object', 'a FAL object'); | |
$this->registerArgument('crop', 'string|bool', 'overrule cropping of image (setting to FALSE disables the cropping set in FileReference)'); | |
$this->registerArgument('cropVariant', 'string', 'select a cropping variant, in case multiple croppings have been specified or stored in FileReference', false, 'default'); | |
$this->registerArgument('sizes', 'string', 'Comma-separated list of image sizes'); | |
$this->registerArgument('ratio', 'string', 'Ratio of the image. This can be a float value (e.g. 1.5) or a ratio string (e.g. 1:2)'); | |
$this->registerArgument('maxWidth', 'int', 'maximum width of the image'); | |
$this->registerArgument('maxHeight', 'int', 'minimum width of the image'); | |
$this->registerArgument('absolute', 'bool', 'Force absolute URL', false, false); | |
} | |
/** | |
* Resizes a given image (if required) and renders the respective img tag | |
* | |
* @see https://docs.typo3.org/typo3cms/TyposcriptReference/ContentObjects/Image/ | |
* | |
* @throws \TYPO3\CMS\Fluid\Core\ViewHelper\Exception | |
* @return string Rendered tag | |
*/ | |
public function render() | |
{ | |
if (is_numeric($this->arguments['ratio'])) { | |
$ratio = $this->arguments['ratio']; | |
} elseif (preg_match(static::RATIO_PATTERN, $this->arguments['ratio'], $matches)) { | |
$ratio = $matches[1] / $matches[2]; | |
} | |
$maximumWidth = $this->arguments['maxWidth']; | |
$maximumHeight = $this->arguments['maxHeight']; | |
$crop = $this->arguments['crop']; | |
$cropVariant = $this->arguments['cropVariant']; | |
if ((is_null($this->arguments['src']) && is_null($this->arguments['image'])) || (!is_null($this->arguments['src']) && !is_null($this->arguments['image']))) { | |
throw new \TYPO3\CMS\Fluid\Core\ViewHelper\Exception('You must either specify a string src or a File object.', 1382284106); | |
} | |
try { | |
$image = $this->imageService->getImage($this->arguments['src'], $this->arguments['image'], $this->arguments['treatIdAsReference']); | |
if ($this->arguments['sizes']) { | |
$sizesCsv = $this->arguments['sizes']; | |
} else { | |
$objectManager = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Object\ObjectManager::class); | |
$configurationManager = $objectManager->get(\TYPO3\CMS\Extbase\Configuration\ConfigurationManager::class); | |
$extbaseFrameworkConfiguration = $configurationManager->getConfiguration(\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface::CONFIGURATION_TYPE_FULL_TYPOSCRIPT); | |
$sizesCsv = $extbaseFrameworkConfiguration['config.']['responsiveImage.']['sizes']; | |
} | |
$sizes = GeneralUtility::intExplode(',', $sizesCsv, true); | |
$srcSetString = $this->srcSetService->getSrcSetAttribute($image, $ratio, $maximumWidth, $maximumHeight, $crop, $cropVariant, $sizes, null); | |
$classNames = ['lazyload']; | |
if (isset($this->arguments['class'])) { | |
$classNames[] = $this->arguments['class']; | |
} | |
$this->tag->addAttributes( | |
[ | |
'class' => implode(' ', $classNames), | |
'data-sizes' => 'auto', | |
'data-srcset' => $srcSetString, | |
'srcset' => 'data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==' | |
] | |
); | |
$alt = $image->getProperty('alternative'); | |
$title = $image->getProperty('title'); | |
// The alt-attribute is mandatory to have valid html-code, therefore add it even if it is empty | |
if (empty($this->arguments['alt'])) { | |
$this->tag->addAttribute('alt', $alt); | |
} | |
if (empty($this->arguments['title']) && $title) { | |
$this->tag->addAttribute('title', $title); | |
} | |
} catch (ResourceDoesNotExistException $e) { | |
// thrown if file does not exist | |
} catch (\UnexpectedValueException $e) { | |
// thrown if a file has been replaced with a folder | |
} catch (\RuntimeException $e) { | |
// RuntimeException thrown if a file is outside of a storage | |
} catch (\InvalidArgumentException $e) { | |
// thrown if file storage does not exist | |
} | |
return $this->tag->render(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Visol\Foobar\Service; | |
/* | |
* This file is inspired by the Visol.Neos.ResponsiveImages package. | |
* | |
* (c) visol digitale Dienstleistungen GmbH, www.visol.ch | |
* | |
* This package is Open Source Software. For the full copyright and license | |
* information, please view the LICENSE file which was distributed with this | |
* source code. | |
*/ | |
use TYPO3\CMS\Core\Imaging\ImageManipulation\CropVariantCollection; | |
use TYPO3\CMS\Core\Resource\AbstractFile; | |
use TYPO3\CMS\Core\Resource\FileInterface; | |
/** | |
* Render the srcset attribute with responsive images. Accepts mostly the same parameters as the uri.image ViewHelper of the Neos.Media package: | |
* asset, maximumWidth, maximumHeight, allowCropping, ratio. | |
* | |
*/ | |
class SrcSetService | |
{ | |
/** | |
* @var \TYPO3\CMS\Extbase\Service\ImageService | |
*/ | |
protected $imageService; | |
/** | |
* @param \TYPO3\CMS\Extbase\Service\ImageService $imageService | |
*/ | |
public function injectImageService(\TYPO3\CMS\Extbase\Service\ImageService $imageService) | |
{ | |
$this->imageService = $imageService; | |
} | |
/** | |
* Returns a processed image path | |
* | |
* @param FileInterface $image | |
* @param float $ratio | |
* @param int $maximumWidth | |
* @param int $maximumHeight | |
* @param boolean $allowCropping | |
* @param int $quality | |
* @param array $sizes | |
* @param RequestInterface $request | |
* @return string | |
* @throws \Exception | |
*/ | |
public function getSrcSetAttribute($image, $ratio, $maximumWidth, $maximumHeight, $crop, $cropVariant, array $sizes, $absolute = false) | |
{ | |
if (!is_array($sizes) || !count($sizes) > 0) { | |
throw new \Exception('No sizes defined.', 1519837126); | |
} | |
if (!$image instanceof FileInterface) { | |
throw new \Exception('No asset given for rendering.', 1519844659); | |
} | |
if ($image->getProperty('type') == AbstractFile::FILETYPE_IMAGE) { | |
$assetWidth = $image->getProperty('width'); | |
$assetHeight = $image->getProperty('height'); | |
} | |
$cropString = $crop; | |
if ($cropString === null && $image->hasProperty('crop') && $image->getProperty('crop')) { | |
$cropString = $image->getProperty('crop'); | |
} | |
$cropVariantCollection = CropVariantCollection::create((string)$cropString); | |
$cropVariant = $cropVariant ?: 'default'; | |
$cropArea = $cropVariantCollection->getCropArea($cropVariant); | |
$srcSetData = []; | |
foreach ($sizes as $size) { | |
$currentWidth = null; | |
$currentMaximumWidth = $size; | |
$currentHeight = null; | |
$currentMaximumHeight = null; | |
if ($currentMaximumWidth > $assetWidth) { | |
continue; | |
} | |
if (isset($maximumWidth) && $currentMaximumWidth > $maximumWidth) { | |
continue; | |
} | |
if ($ratio) { | |
$currentWidth = $currentMaximumWidth; | |
$currentMaximumHeight = $size / $ratio; | |
$currentHeight = $currentMaximumHeight; | |
if ($currentMaximumHeight > $assetHeight) { | |
continue; | |
} | |
if (isset($maximumHeight) && $currentMaximumHeight > $maximumHeight) { | |
continue; | |
} | |
} | |
$processingInstructions = [ | |
'width' => $currentWidth . 'c', | |
'height' => $currentHeight . 'c', | |
'maxWidth' => $currentMaximumWidth, | |
'maxHeight' => $currentMaximumHeight, | |
'crop' => $cropArea->isEmpty() ? null : $cropArea->makeAbsoluteBasedOnFile($image), | |
]; | |
$processedImage = $this->imageService->applyProcessingInstructions($image, $processingInstructions); | |
$imageUri = $this->imageService->getImageUri($processedImage, $absolute); | |
if ($imageUri === null) { | |
continue; | |
} | |
$srcSetData[] = $imageUri . ' ' . $processedImage->getProperty('width') . 'w ' . $processedImage->getProperty('height') . 'h '; | |
} | |
return implode(', ', $srcSetData); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Visol\Foobar\ViewHelpers; | |
/* * | |
* This script is part of the TYPO3 project - inspiring people to share! * | |
* * | |
* TYPO3 is free software; you can redistribute it and/or modify it under * | |
* the terms of the GNU General Public License version 2 as published by * | |
* the Free Software Foundation. * | |
* * | |
* This script is distributed in the hope that it will be useful, but * | |
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- * | |
* TABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General * | |
* Public License for more details. * | |
* */ | |
use TYPO3\CMS\Core\Resource\Exception\ResourceDoesNotExistException; | |
use TYPO3\CMS\Core\Utility\GeneralUtility; | |
/** | |
* Resizes a given image (if required) and renders the respective img tag | |
* | |
* = Examples = | |
* | |
* <code title="Default"> | |
* <f:image src="EXT:myext/Resources/Public/typo3_logo.png" alt="alt text" /> | |
* </code> | |
* <output> | |
* <img alt="alt text" src="typo3conf/ext/myext/Resources/Public/typo3_logo.png" width="396" height="375" /> | |
* or (in BE mode): | |
* <img alt="alt text" src="../typo3conf/ext/viewhelpertest/Resources/Public/typo3_logo.png" width="396" height="375" /> | |
* </output> | |
* | |
* <code title="Image Object"> | |
* <f:image image="{imageObject}" /> | |
* </code> | |
* <output> | |
* <img alt="alt set in image record" src="fileadmin/_processed_/323223424.png" width="396" height="375" /> | |
* </output> | |
* | |
* <code title="Inline notation"> | |
* {f:image(src: 'EXT:viewhelpertest/Resources/Public/typo3_logo.png', alt: 'alt text', minWidth: 30, maxWidth: 40)} | |
* </code> | |
* <output> | |
* <img alt="alt text" src="../typo3temp/assets/images/f13d79a526.png" width="40" height="38" /> | |
* (depending on your TYPO3s encryption key) | |
* </output> | |
* | |
* <code title="Other resource type (e.g. PDF)"> | |
* <f:image src="fileadmin/user_upload/example.pdf" alt="foo" /> | |
* </code> | |
* <output> | |
* If your graphics processing library is set up correctly then it will output a thumbnail of the first page of your PDF document. | |
* <img src="fileadmin/_processed_/1/2/csm_example_aabbcc112233.gif" width="200" height="284" alt="foo"> | |
* </output> | |
* | |
* <code title="Non-existent image"> | |
* <f:image src="NonExistingImage.png" alt="foo" /> | |
* </code> | |
* <output> | |
* Could not get image resource for "NonExistingImage.png". | |
* </output> | |
*/ | |
class SrcSetViewHelper extends \TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHelper | |
{ | |
const RATIO_PATTERN = '/(\d):(\d)/'; | |
/** | |
* @var \TYPO3\CMS\Extbase\Service\ImageService | |
*/ | |
protected $imageService; | |
/** | |
* @var \Visol\Foobar\Service\SrcSetService | |
*/ | |
protected $srcSetService; | |
/** | |
* @param \TYPO3\CMS\Extbase\Service\ImageService $imageService | |
*/ | |
public function injectImageService(\TYPO3\CMS\Extbase\Service\ImageService $imageService) | |
{ | |
$this->imageService = $imageService; | |
} | |
/** | |
* @param \Visol\Foobar\Service\SrcSetService $srcSetService | |
*/ | |
public function injectSrcSetService(\Visol\Foobar\Service\SrcSetService $srcSetService) | |
{ | |
$this->srcSetService = $srcSetService; | |
} | |
/** | |
* Initialize arguments. | |
*/ | |
public function initializeArguments() | |
{ | |
$this->registerArgument( | |
'src', | |
'string', | |
'a path to a file, a combined FAL identifier or an uid (int). | |
If $treatIdAsReference is set, the integer is considered the uid of the sys_file_reference record. | |
If you already got a FAL object, consider using the $image parameter instead' | |
); | |
$this->registerArgument('treatIdAsReference', 'bool', 'given src argument is a sys_file_reference record'); | |
$this->registerArgument('image', 'object', 'a FAL object'); | |
$this->registerArgument('crop', 'string|bool', 'overrule cropping of image (setting to FALSE disables the cropping set in FileReference)'); | |
$this->registerArgument('cropVariant', 'string', 'select a cropping variant, in case multiple croppings have been specified or stored in FileReference', false, 'default'); | |
$this->registerArgument('sizes', 'string', 'Comma-separated list of image sizes'); | |
$this->registerArgument('ratio', 'string', 'Ratio of the image. This can be a float value (e.g. 1.5) or a ratio string (e.g. 1:2)'); | |
$this->registerArgument('maxWidth', 'int', 'minimum width of the image'); | |
$this->registerArgument('maxHeight', 'int', 'minimum width of the image'); | |
$this->registerArgument('absolute', 'bool', 'Force absolute URL', false, false); | |
} | |
/** | |
* Resizes a given image (if required) and renders the respective img tag | |
* | |
* @see https://docs.typo3.org/typo3cms/TyposcriptReference/ContentObjects/Image/ | |
* | |
* @throws \TYPO3\CMS\Fluid\Core\ViewHelper\Exception | |
* @return string Rendered tag | |
*/ | |
public function render() | |
{ | |
if (is_numeric($this->arguments['ratio'])) { | |
$ratio = $this->arguments['ratio']; | |
} elseif (preg_match(static::RATIO_PATTERN, $this->arguments['ratio'], $matches)) { | |
$ratio = $matches[1] / $matches[2]; | |
} | |
$maximumWidth = $this->arguments['maxWidth']; | |
$maximumHeight = $this->arguments['maxHeight']; | |
$crop = $this->arguments['crop']; | |
$cropVariant = $this->arguments['cropVariant']; | |
if ((is_null($this->arguments['src']) && is_null($this->arguments['image'])) || (!is_null($this->arguments['src']) && !is_null($this->arguments['image']))) { | |
throw new \TYPO3\CMS\Fluid\Core\ViewHelper\Exception('You must either specify a string src or a File object.', 1382284106); | |
} | |
try { | |
$image = $this->imageService->getImage($this->arguments['src'], $this->arguments['image'], $this->arguments['treatIdAsReference']); | |
if ($this->arguments['sizes']) { | |
$sizesCsv = $this->arguments['sizes']; | |
} else { | |
$objectManager = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Object\ObjectManager::class); | |
$configurationManager = $objectManager->get(\TYPO3\CMS\Extbase\Configuration\ConfigurationManager::class); | |
$extbaseFrameworkConfiguration = $configurationManager->getConfiguration(\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface::CONFIGURATION_TYPE_FULL_TYPOSCRIPT); | |
$sizesCsv = $extbaseFrameworkConfiguration['config.']['responsiveImage.']['sizes']; | |
} | |
$sizes = GeneralUtility::intExplode(',', $sizesCsv, true); | |
$srcSetString = $this->srcSetService->getSrcSetAttribute($image, $ratio, $maximumWidth, $maximumHeight, $crop, $cropVariant, $sizes, null); | |
} catch (ResourceDoesNotExistException $e) { | |
// thrown if file does not exist | |
} catch (\UnexpectedValueException $e) { | |
// thrown if a file has been replaced with a folder | |
} catch (\RuntimeException $e) { | |
// RuntimeException thrown if a file is outside of a storage | |
} catch (\InvalidArgumentException $e) { | |
// thrown if file storage does not exist | |
} | |
return $srcSetString; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment