Skip to content

Commit

Permalink
feat: Add .imgTag(), .pictureTag(), and .linkPreloadTag() to th…
Browse files Browse the repository at this point in the history
…e `OptimizedImage` model
khalwat committed Apr 1, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
1 parent 6c13ec8 commit 01d08ca
Showing 7 changed files with 609 additions and 0 deletions.
96 changes: 96 additions & 0 deletions src/models/BaseImageTag.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<?php
/**
* Image Optimize plugin for Craft CMS
*
* Automatically optimize images after they've been transformed
*
* @link https://nystudio107.com
* @copyright Copyright (c) nystudio107
*/

namespace nystudio107\imageoptimize\models;

/**
* @author nystudio107
* @package ImageOptimize
* @since 5.0.0-beta.1
*/
abstract class BaseImageTag extends BaseTag
{
/**
* Swap the tag attributes to work with lazy loading
* ref: https://web.dev/native-lazy-loading/#how-do-i-handle-browsers-that-don't-yet-support-native-lazy-loading
*
* @param string $loading 'eager', 'lazy', 'lazySizes', 'lazySizesFallback'
* @param string $placeHolder 'box', 'color', 'image', 'silhouette'
* @param array $attrs
*
* @return array
*/
protected function swapLazyLoadAttrs(string $loading, string $placeHolder, array $attrs): array
{
// Set the class and loading attributes
if (isset($attrs['class'])) {
$attrs['class'] = trim($attrs['class'] . ' lazyload');
}
// Set the style on this element to be the placeholder image as the background-image
if (isset($attrs['style']) && !empty($attrs['src'])) {
$attrs['style'] = trim(
$attrs['style'] .
'background-image:url(' . $this->getLazyLoadSrc($placeHolder) . '); background-size: cover;'
);
}
// Handle attributes that lazy and lazySizesFallback have in common
switch ($loading) {
case 'lazy':
case 'lazySizesFallback':
if (isset($attrs['loading'])) {
$attrs['loading'] = 'lazy';
}
break;
default:
break;
}
// Handle attributes that lazySizes and lazySizesFallback have in common
switch ($loading) {
case 'lazySizes':
case 'lazySizesFallback':
// Only swap to data- attributes if they want the LazySizes fallback
if (!empty($attrs['sizes'])) {
$attrs['data-sizes'] = $attrs['sizes'];
$attrs['sizes'] = '';
}
if (!empty($attrs['srcset'])) {
$attrs['data-srcset'] = $attrs['srcset'];
$attrs['srcset'] = '';
}
if (!empty($attrs['src'])) {
$attrs['data-src'] = $attrs['src'];
$attrs['src'] = $this->getLazyLoadSrc($placeHolder);
}
break;
default:
break;
}

return $attrs;
}

/**
* Return a lazy loading placeholder image based on the passed in $lazyload setting
*
* @param string $lazyLoad
*
* @return string
*/
protected function getLazyLoadSrc(string $lazyLoad): string
{
$lazyLoad = strtolower($lazyLoad);
return match ($lazyLoad) {
'image' => $this->optimizedImage->getPlaceholderImage(),
'silhouette' => $this->optimizedImage->getPlaceholderSilhouette(),
'color' => $this->optimizedImage->getPlaceholderBox($this->colorPalette[0] ?? null),
default => $this->optimizedImage->getPlaceholderBox('#CCC'),
};
}
}
43 changes: 43 additions & 0 deletions src/models/BaseTag.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php
/**
* Image Optimize plugin for Craft CMS
*
* Automatically optimize images after they've been transformed
*
* @link https://nystudio107.com
* @copyright Copyright (c) nystudio107
*/

namespace nystudio107\imageoptimize\models;

use craft\base\Model;
use craft\helpers\Template;
use Twig\Markup;

/**
* @author nystudio107
* @package ImageOptimize
* @since 5.0.0-beta.1
*/
abstract class BaseTag extends Model implements TagInterface
{
use TagTrait;

/**
* @return string
*/
public function __toString(): string
{
return $this->render();
}

/**
* Render the tag
*
* @return Markup
*/
public function render(): Markup
{
return Template::raw('');
}
}
116 changes: 116 additions & 0 deletions src/models/ImgTag.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
<?php
/**
* Image Optimize plugin for Craft CMS
*
* Automatically optimize images after they've been transformed
*
* @link https://nystudio107.com
* @copyright Copyright (c) nystudio107
*/

namespace nystudio107\imageoptimize\models;

use craft\helpers\Html;
use craft\helpers\Template;
use Twig\Markup;

/**
* @author nystudio107
* @package ImageOptimize
* @since 5.0.0-beta.1
*/
class ImgTag extends BaseImageTag
{
/**
* @var string The loading scheme to use: 'eager', 'lazy', 'lazySizes', 'lazySizesFallback'
*/
public string $loading = 'eager';

/**
* @var string The type of placeholder image to use: 'box', 'color', 'image', 'silhouette'
*/
public string $placeholder = 'box';

/**
* @var array array of tag attributes for the <img> tag
*/
public array $imgAttrs = [];

/**
* @param $config
*/
public function __construct($config = [])
{
parent::__construct($config);
// Populate the $imageAttrs
$this->imgAttrs = [
'class' => '',
'style' => '',
'width' => $this->optimizedImage->placeholderWidth,
'height' => $this->optimizedImage->placeholderHeight,
'src' => reset($this->optimizedImage->optimizedImageUrls),
'srcset' => $this->optimizedImage->getSrcsetFromArray($this->optimizedImage->optimizedImageUrls),
'sizes' => '100vw',
'loading' => '',
];
}

/**
* Set the $loading property
*
* @param string $value
* @return $this
*/
public function loading(string $value): ImgTag
{
$this->loading = $value;

return $this;
}

/**
* Set the $placeholder property
*
* @param string $value
* @return $this
*/
public function placeholder(string $value): ImgTag
{
$this->placeholder = $value;

return $this;
}

/**
* Merge the passed array of tag attributes into $imgAttrs
*
* @param array $value
* @return $this
*/
public function imgAttrs(array $value): ImgTag
{
$this->imgAttrs = array_merge($this->imgAttrs, $value);

return $this;
}

/**
* Generate a complete <img> tag for the $optimizedImage OptimizedImage model
*
* @return Markup
*/
public function render(): Markup
{
$attrs = $this->imgAttrs;
// Handle lazy loading
if ($this->loading !== 'eager') {
$attrs = $this->swapLazyLoadAttrs($this->loading, $this->placeholder, $attrs);
}
// Remove any empty attributes
$attrs = array_filter($attrs);
// Render the tag
$tag = Html::tag('img', '', $attrs);

return Template::raw($tag);
}
}
79 changes: 79 additions & 0 deletions src/models/LinkPreloadTag.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?php
/**
* Image Optimize plugin for Craft CMS
*
* Automatically optimize images after they've been transformed
*
* @link https://nystudio107.com
* @copyright Copyright (c) nystudio107
*/

namespace nystudio107\imageoptimize\models;

use craft\helpers\Html;
use craft\helpers\Template;
use Twig\Markup;

/**
* @author nystudio107
* @package ImageOptimize
* @since 5.0.0-beta.1
*/
class LinkPreloadTag extends BaseTag
{
/**
* @var array array of tag attributes for the <link rel="preload"> tag
*/
public array $linkAttrs = [];

/**
* @param $config
*/
public function __construct($config = [])
{
parent::__construct($config);
// Any web browser that supports link rel="preload" as="image" also supports webp, so prefer that
$srcset = $this->optimizedImage->optimizedImageUrls;
if (!empty($this->optimizedImage->optimizedWebPImageUrls)) {
$srcset = $this->optimizedImage->optimizedWebPImageUrls;
}
// Populate the $imageAttrs
$this->linkAttrs = [
'rel' => 'preload',
'as' => 'image',
'href' => reset($srcset),
'imagesrcset' => $this->optimizedImage->getSrcsetFromArray($srcset),
'imagesizes' => '100vw',
];
}

/**
* Merge the passed array of tag attributes into $linkAttrs
*
* @param array $value
* @return $this
*/
public function linkAttrs(array $value): LinkPreloadTag
{
$this->linkAttrs = array_merge($this->linkAttrs, $value);

return $this;
}

/**
* Generate a complete <link rel="preload"> tag for the $optimizedImage OptimizedImage model
* ref: https://web.dev/preload-responsive-images/#imagesrcset-and-imagesizes
*
* @return Markup
*/
public function render(): Markup
{
$attrs = $this->linkAttrs;
// Remove any empty attributes
$attrs = array_filter($attrs);
// Render the tag
$tag = Html::tag('link', '', $attrs);

return Template::raw($tag);
}
}
223 changes: 223 additions & 0 deletions src/models/PictureTag.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
<?php
/**
* Image Optimize plugin for Craft CMS
*
* Automatically optimize images after they've been transformed
*
* @link https://nystudio107.com
* @copyright Copyright (c) nystudio107
*/

namespace nystudio107\imageoptimize\models;

use craft\helpers\Html;
use craft\helpers\Template;
use Twig\Markup;

/**
* @author nystudio107
* @package ImageOptimize
* @since 5.0.0-beta.1
*/
class PictureTag extends BaseImageTag
{
/**
* @var string The loading scheme to use: 'eager', 'lazy', 'lazySizes', 'lazySizesFallback'
*/
public string $loading = 'eager';

/**
* @var string The type of placeholder image to use: 'box', 'color', 'image', 'silhouette'
*/
public string $placeholder = 'box';

/**
* @var array array of tag attributes for the <picture> tag
*/
public array $pictureAttrs = [];

/**
* @var array array of tag attributes for the <srcset> tag
*/
public array $srcsetAttrs = [];

/**
* @var array array of tag attributes for the <img> tag
*/
public array $imgAttrs = [];

/**
* @var OptimizedImage[] array OptimizedImage models to add as art direction
*/
public array $artDirection = [];

/**
* @param $config
*/
public function __construct($config = [])
{
parent::__construct($config);
// Populate the $imageAttrs
$this->imgAttrs = [
'class' => '',
'style' => '',
'width' => $this->optimizedImage->placeholderWidth,
'height' => $this->optimizedImage->placeholderHeight,
'src' => reset($this->optimizedImage->optimizedImageUrls),
'loading' => '',
];
// Populate the $srcsetAttrs
$this->populateSrcsetAttrs($this->optimizedImage, []);
// Populate the $pictureAttrs
$this->pictureAttrs = [];
}

/**
* Set the $loading property
*
* @param string $value
* @return $this
*/
public function loading(string $value): PictureTag
{
$this->loading = $value;

return $this;
}

/**
* Set the $placeholder property
*
* @param string $value
* @return $this
*/
public function placeholder(string $value): PictureTag
{
$this->placeholder = $value;

return $this;
}

/**
* Merge the passed array of tag attributes into $pictureAttrs
*
* @param array $value
* @return $this
*/
public function pictureAttrs(array $value): PictureTag
{
$this->pictureAttrs = array_merge($this->pictureAttrs, $value);

return $this;
}

/**
* Merge the passed array of tag attributes into $srcsetAttrs
*
* @param array $value
* @return $this
*/
public function srcsetAttrs(array $value): PictureTag
{
foreach ($this->srcsetAttrs as &$attrs) {
$attrs = array_merge($attrs, $value);
}
unset($attrs);

return $this;
}

/**
* Merge the passed array of tag attributes into $imgAttrs
*
* @param array $value
* @return $this
*/
public function imgAttrs(array $value): PictureTag
{
$this->imgAttrs = array_merge($this->imgAttrs, $value);

return $this;
}

/**
* Add art direction srcsets to the $srcsetAttrs
*
* @param OptimizedImage $optimizedImage
* @param array $srcsetAttrs
* @return void
*/
public function artDirection(OptimizedImage $optimizedImage, array $srcsetAttrs = []): PictureTag
{
$this->populateSrcsetAttrs($optimizedImage, $srcsetAttrs);

return $this;
}

/**
* Generate a complete <img> tag for the $optimizedImage OptimizedImage model
*
* @return Markup
*/
public function render(): Markup
{
$content = '';
// Handle the <srcset> tag(s)
foreach ($this->srcsetAttrs as $attrs) {
// Handle lazy loading
if ($this->loading !== 'eager') {
$attrs = $this->swapLazyLoadAttrs($this->loading, $this->placeholder, $attrs);
}
// Remove any empty attributes
$attrs = array_filter($attrs);
// Render the tag
$content .= Html::tag('source', '', $attrs);
}
// Handle the <img> tag
$attrs = $this->imgAttrs;
// Handle lazy loading
if ($this->loading !== 'eager') {
$attrs = $this->swapLazyLoadAttrs($this->loading, $this->placeholder, $attrs);
}
// Remove any empty attributes
$attrs = array_filter($attrs);
// Render the tag
$content .= Html::tag('img', '', $attrs);
// Handle the <picture> tag
$attrs = $this->pictureAttrs;
// Remove any empty attributes
$attrs = array_filter($attrs);
// Render the tag
$tag = Html::tag('picture', $content, $attrs);

return Template::raw($tag);
}

/**
* Populate the $srcsetAttrs from the passed in $optimizedImage and $sizes
*
* @param OptimizedImage $optimizedImage
* @param array $srcsetAttrs attributes to add to the $srcsetAttrs array
* @return void
*/
protected function populateSrcsetAttrs(OptimizedImage $optimizedImage, array $srcsetAttrs): void
{
if (!empty($optimizedImage->optimizedWebPImageUrls)) {
$this->srcsetAttrs[] = array_merge([
'media' => '',
'srcset' => $optimizedImage->getSrcsetFromArray($optimizedImage->optimizedWebPImageUrls),
'type' => 'image/webp',
'sizes' => '100vw',
'width' => $optimizedImage->placeholderWidth,
'height' => $optimizedImage->placeholderHeight,
], $srcsetAttrs);
}
$this->srcsetAttrs[] = array_merge([
'media' => '',
'srcset' => $optimizedImage->getSrcsetFromArray($optimizedImage->optimizedImageUrls),
'sizes' => '100vw',
'width' => $optimizedImage->placeholderWidth,
'height' => $optimizedImage->placeholderHeight,
], $srcsetAttrs);
}
}
28 changes: 28 additions & 0 deletions src/models/TagInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php
/**
* Image Optimize plugin for Craft CMS
*
* Automatically optimize images after they've been transformed
*
* @link https://nystudio107.com
* @copyright Copyright (c) nystudio107
*/

namespace nystudio107\imageoptimize\models;

use Twig\Markup;

/**
* @author nystudio107
* @package ImageOptimize
* @since 5.0.0-beta.1
*/
interface TagInterface
{
/**
* Render the tag
*
* @return Markup
*/
public function render(): Markup;
}
24 changes: 24 additions & 0 deletions src/models/TagTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php
/**
* Image Optimize plugin for Craft CMS
*
* Automatically optimize images after they've been transformed
*
* @link https://nystudio107.com
* @copyright Copyright (c) nystudio107
*/

namespace nystudio107\imageoptimize\models;

/**
* @author nystudio107
* @package ImageOptimize
* @since 5.0.0-beta.1
*/
trait TagTrait
{
/**
* @var ?OptimizedImage
*/
public ?OptimizedImage $optimizedImage = null;
}

0 comments on commit 01d08ca

Please sign in to comment.