- Sponsor
-
Notifications
You must be signed in to change notification settings - Fork 36
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add
.imgTag()
, .pictureTag()
, and .linkPreloadTag()
to th…
…e `OptimizedImage` model
Showing
7 changed files
with
609 additions
and
0 deletions.
There are no files selected for viewing
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
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'), | ||
}; | ||
} | ||
} |
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
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(''); | ||
} | ||
} |
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
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); | ||
} | ||
} |
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
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); | ||
} | ||
} |
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
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); | ||
} | ||
} |
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
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; | ||
} |
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
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; | ||
} |