Skip to content

Commit

Permalink
Merge pull request #1 from justbetter/feature/wip-features
Browse files Browse the repository at this point in the history
Added attribute bag + option for sources in config
  • Loading branch information
kevinmeijer97 authored May 31, 2024
2 parents 8b63e1d + e160e80 commit 8d5f7c0
Show file tree
Hide file tree
Showing 5 changed files with 156 additions and 53 deletions.
26 changes: 16 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ This package adds a blade directive. You can put a Asset in the blade directive
@responsive($image, [
'alt' => 'This is an alt text.',
'class' => 'some classes here',
'lazy' => true
'loading' => 'lazy'
])
```

Expand All @@ -32,23 +32,29 @@ The package has some default config. By default it will use the presets defined
Default config:
```php
'presets' => [
'placeholder' => ['w' => 32, 'h' => 32, 'q' => 100, 'fit' => 'crop'],
'xs' => ['w' => 320, 'h' => 320, 'q' => 100, 'fit' => 'crop'],
'sm' => ['w' => 480, 'h' => 480, 'q' => 100, 'fit' => 'crop'],
'md' => ['w' => 768, 'h' => 768, 'q' => 100, 'fit' => 'crop'],
'lg' => ['w' => 1280, 'h' => 1280, 'q' => 100, 'fit' => 'crop'],
'xl' => ['w' => 1440, 'h' => 1440, 'q' => 100, 'fit' => 'crop'],
'2xl' => ['w' => 1680, 'h' => 1680, 'q' => 100, 'fit' => 'crop'],
'placeholder' => ['w' => 32, 'h' => 32, 'q' => 100, 'fit' => 'crop_focal'],
'xs' => ['w' => 320, 'h' => 320, 'q' => 100, 'fit' => 'crop_focal'],
'sm' => ['w' => 480, 'h' => 480, 'q' => 100, 'fit' => 'crop_focal'],
'md' => ['w' => 768, 'h' => 768, 'q' => 100, 'fit' => 'crop_focal'],
'lg' => ['w' => 1280, 'h' => 1280, 'q' => 100, 'fit' => 'crop_focal'],
'xl' => ['w' => 1440, 'h' => 1440, 'q' => 100, 'fit' => 'crop_focal'],
'2xl' => ['w' => 1680, 'h' => 1680, 'q' => 100, 'fit' => 'crop_focal'],
],
```

### Placeholder
On pageload a small variant of the image will be loaded, if you don't want this you can disable the placeholder in the config file.
```php
'placeholder' => true,
'placeholder' => true,
```

### Sources
Configure which sources you would like to use. By default we only use webp sources, it's also possible to only configure sources based on the image mime type or you can make use of both.
```php
'sources' => 'webp',
```

### Publish
```
php artisan vendor:publish --provider="JustBetter\ImageOptimize\ServiceProvider"
```
```
33 changes: 25 additions & 8 deletions config/glide-directive.php
Original file line number Diff line number Diff line change
@@ -1,15 +1,32 @@
<?php

return [
// Make use of the placeholder image which is generated from a glide data url.
// This will add a blurry image as a placeholder before loading the correct size.
'placeholder' => true,

// The default presets used for generating the resizes.
// If the config in statamic.assets.image_manipulation.presets is empty this will be used instead.
'presets' => [
'placeholder' => ['w' => 32, 'h' => 32, 'q' => 100, 'fit' => 'crop'],
'xs' => ['w' => 320, 'h' => 320, 'q' => 100, 'fit' => 'crop'],
'sm' => ['w' => 480, 'h' => 480, 'q' => 100, 'fit' => 'crop'],
'md' => ['w' => 768, 'h' => 768, 'q' => 100, 'fit' => 'crop'],
'lg' => ['w' => 1280, 'h' => 1280, 'q' => 100, 'fit' => 'crop'],
'xl' => ['w' => 1440, 'h' => 1440, 'q' => 100, 'fit' => 'crop'],
'2xl' => ['w' => 1680, 'h' => 1680, 'q' => 100, 'fit' => 'crop'],
'placeholder' => ['w' => 32, 'h' => 32, 'q' => 100, 'fit' => 'crop_focal'],
'xs' => ['w' => 320, 'h' => 320, 'q' => 100, 'fit' => 'crop_focal'],
'sm' => ['w' => 480, 'h' => 480, 'q' => 100, 'fit' => 'crop_focal'],
'md' => ['w' => 768, 'h' => 768, 'q' => 100, 'fit' => 'crop_focal'],
'lg' => ['w' => 1280, 'h' => 1280, 'q' => 100, 'fit' => 'crop_focal'],
'xl' => ['w' => 1440, 'h' => 1440, 'q' => 100, 'fit' => 'crop_focal'],
'2xl' => ['w' => 1680, 'h' => 1680, 'q' => 100, 'fit' => 'crop_focal'],

'xs-h' => ['w' => 160, 'h' => 320, 'q' => 100, 'fit' => 'crop_focal'],
'sm-h' => ['w' => 320, 'h' => 480, 'q' => 100, 'fit' => 'crop_focal'],
'md-h' => ['w' => 480, 'h' => 768, 'q' => 100, 'fit' => 'crop_focal'],
'lg-h' => ['w' => 768, 'h' => 1280, 'q' => 100, 'fit' => 'crop_focal'],
'xl-h' => ['w' => 1280, 'h' => 1440, 'q' => 100, 'fit' => 'crop_focal'],
'2xl-h' => ['w' => 1440, 'h' => 1680, 'q' => 100, 'fit' => 'crop_focal'],
],
];

// Configure which sources you would like to use.
// Set 'webp' for WebP only.
// Set 'mime_type' for the original image mime type.
// Set 'both' to use both sources.
'sources' => 'webp'
];
46 changes: 25 additions & 21 deletions resources/views/image.blade.php
Original file line number Diff line number Diff line change
@@ -1,35 +1,39 @@
@if($image)
<picture>
@if( $image->extension() == 'svg' || $image->extension() == 'gif')
<img class="{{ $class }}" src="{{ $image->url() }}" alt="{{ $alt }}"
@if($lazy)
loading="lazy"
@endif
/>
<img
class="{{ $class }}"
src="{{ $image->url() }}"
alt="{{ $alt }}"
width="{{ $width }}"
height="{{ $height }}"
/>
@else
<source
srcset="{{ $presets['webp'] }}"
sizes="32px"
type="image/webp"
>
<source
srcset="{{ $presets[$image->mimeType()] }}"
sizes="32px"
type="{{ $image->mimeType() }}"
>
@isset($presets['webp'])
<source
srcset="{{ $presets['webp'] }}"
sizes="32px"
type="image/webp"
>
@endisset
@isset($presets[$image->mimeType()])
<source
srcset="{{ $presets[$image->mimeType()] }}"
sizes="32px"
type="{{ $image->mimeType() }}"
>
@endisset
<img
{!! $attributes ?? '' !!}
class="{{ $class }}"
src="{{ $presets['placeholder'] }}"
src="{{ $presets['placeholder'] ?? $image->url() }}"
alt="{{ $alt ?? $image->alt() }}"
width="{{ $image->width() }}"
height="{{ $image->height() }}"
width="{{ $width }}"
height="{{ $height }}"
onload="
this.onload=null;
window.responsiveResizeObserver.observe(this);
"
@if($lazy)
loading="lazy"
@endif
>
@endif
</picture>
Expand Down
9 changes: 5 additions & 4 deletions resources/views/partials/head.blade.php
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
<script>
window.responsiveResizeObserver = new ResizeObserver((entries) => {
console.log('test')
entries.forEach(entry => {
const imgWidth = entry.target.getBoundingClientRect().width;
const imgHeight = entry.target.getBoundingClientRect().height;
const pixelRatio = window.devicePixelRatio * imgWidth;
entry.target.parentNode.querySelectorAll('source').forEach((source) => {
source.sizes = imgWidth + 'px';
source.sizes = pixelRatio + 'px';
});
});
});
</script>
</script>
95 changes: 85 additions & 10 deletions src/Responsive.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@ public static function handle(...$arguments)

return view('statamic-glide-directive::image', [
'image' => $image,
'presets' => Responsive::getPresets($image),
'presets' => self::getPresets($image),
'attributes' => self::getAttributeBag($arguments),
'class' => $arguments['class'] ?? '',
'alt' => $arguments['alt'] ?? '',
'lazy' => $arguments['lazy'] ?? '',
'width' => $arguments['width'] ?? $image->width(),
'height' => $arguments['height'] ?? $image->height(),
]);
}

Expand All @@ -31,22 +33,95 @@ public static function getPresets(Asset $image)
}

$presets = [];
$presets['webp'] = '';
$presets[$image->mimeType()] = '';

foreach ($config as $preset => $data) {
$presets['webp'] .= Statamic::tag($preset === 'placeholder' ? 'glide:data_url' : 'glide')->params(['preset' => $preset, 'src' => $image->url(), 'format' => 'webp', 'fit' => 'crop_focal'])->fetch() . ' ' . $data['w'] . 'w,';
$presets[$image->mimeType()] .= Statamic::tag($preset === 'placeholder' ? 'glide:data_url' : 'glide')->params(['preset' => $preset, 'src' => $image->url(), 'fit' => 'crop_focal'])->fetch() . ' ' . $data['w'] . 'w,';
if (self::canUseWebpSource()) {
$presets['webp'] = '';
}

if (self::canUseMimeTypeSource()) {
$presets[$image->mimeType()] = '';
}

$configPresets = self::getPresetsByRatio($image, $config);

$index = 0;
foreach ($configPresets as $preset => $data) {
$size = $data['w'] . 'w';

if ($index < (count($configPresets) - 1)) {
$size .= ', ';
}

if (self::canUseWebpSource()) {
$glideUrl = Statamic::tag($preset === 'placeholder' ? 'glide:data_url' : 'glide')->params(['preset' => $preset, 'src' => $image->url(), 'format' => 'webp', 'fit' => $data['crop'] ?? 'crop_focal'])->fetch();
if ($glideUrl) {
$presets['webp'] .= $glideUrl . ' ' . $size;
}
}

if (self::canUseMimeTypeSource()) {
$glideUrl = Statamic::tag($preset === 'placeholder' ? 'glide:data_url' : 'glide')->params(['preset' => $preset, 'src' => $image->url(), 'fit' => $data['crop'] ?? 'crop_focal'])->fetch();
if ($glideUrl) {
$presets[$image->mimeType()] .= $glideUrl . ' ' . $size;
}
}


if ($preset === 'placeholder') {
$presets['placeholder'] = Statamic::tag('glide:data_url')->params(['preset' => 'placeholder', 'src' => $image->url(), 'fit' => 'crop_focal'])->fetch();
$glideUrl = Statamic::tag('glide:data_url')->params(['preset' => 'placeholder', 'src' => $image->url(), 'fit' => $data['crop'] ?? 'crop_focal'])->fetch();
if ($glideUrl) {
$presets['placeholder'] = $glideUrl;
}
}

$index++;
}

if (!isset($presets['placeholder'])) {
$presets['placeholder'] = Statamic::tag('glide:data_url')->params(['preset' => collect($config)->keys()->first(), 'src' => $image->url(), 'fit' => 'crop_focal'])->fetch();
$glideUrl = Statamic::tag('glide:data_url')->params(['preset' => collect($configPresets)->keys()->first(), 'src' => $image->url(), 'fit' => 'crop_focal'])->fetch();
$presets['placeholder'] = $glideUrl;
}

return $presets;
}
}

protected static function getPresetsByRatio(Asset $image, array $config): array
{
$ratio = $image->width() / $image->height();
$presets = collect($config);

// filter config based on aspect ratio
// if ratio < 1 get all presets with a height bigger than the width, else get all presets with width equal or bigger than the height.
if ($ratio < 0.999) {
$presets = $presets->filter(fn($preset, $key) => $preset['h'] > $preset['w']);
if ($presets->isNotEmpty() && isset($config['placeholder'])) {
$presets->prepend($config['placeholder'], 'placeholder');
}
} else {
$presets = $presets->filter(fn($preset, $key) => $key === 'placeholder' || $preset['w'] >= $preset['h']);
}

return $presets->isNotEmpty() ? $presets->toArray() : $config;
}

protected static function getAttributeBag(array $arguments): string
{
$excludedAttributes = ['src', 'class', 'alt', 'width', 'height', 'onload'];

return collect($arguments)
->filter(fn ($value, $key) => !in_array($key, $excludedAttributes))
->map(function ($value, $key) {
return $key . '="' . $value . '"';
})->implode(' ');
}

protected static function canUseWebpSource(): bool
{
return in_array(config('justbetter.glide-directive.sources'), ['webp', 'both']);
}

protected static function canUseMimeTypeSource(): bool
{
return in_array(config('justbetter.glide-directive.sources'), ['mime_type', 'both']);
}
}

0 comments on commit 8d5f7c0

Please sign in to comment.