mirror of
https://github.com/grocy/grocy.git
synced 2025-04-28 17:23:56 +00:00
Lazy load all images to increase page load times (references #275)
This commit is contained in:
parent
d209c0bd22
commit
918f84f568
@ -59,13 +59,13 @@ class FilesApiController extends BaseApiController
|
||||
|
||||
if ($forceServeAs == FilesService::FILE_SERVE_TYPE_PICTURE)
|
||||
{
|
||||
$bestFitHeight = 999999;
|
||||
$bestFitHeight = null;
|
||||
if (isset($request->getQueryParams()['best_fit_height']) && !empty($request->getQueryParams()['best_fit_height']) && is_numeric($request->getQueryParams()['best_fit_height']))
|
||||
{
|
||||
$bestFitHeight = $request->getQueryParams()['best_fit_height'];
|
||||
}
|
||||
|
||||
$bestFitWidth = 999999;
|
||||
$bestFitWidth = null;
|
||||
if (isset($request->getQueryParams()['best_fit_width']) && !empty($request->getQueryParams()['best_fit_width']) && is_numeric($request->getQueryParams()['best_fit_width']))
|
||||
{
|
||||
$bestFitWidth = $request->getQueryParams()['best_fit_width'];
|
||||
|
@ -63,7 +63,7 @@ class RecipesController extends BaseController
|
||||
'selectedRecipe' => $selectedRecipe,
|
||||
'selectedRecipePositionsResolved' => $selectedRecipePositionsResolved,
|
||||
'products' => $this->Database->products(),
|
||||
'quantityunits' => $this->Database->quantity_units(),
|
||||
'quantityUnits' => $this->Database->quantity_units(),
|
||||
'selectedRecipeSubRecipes' => $selectedRecipeSubRecipes,
|
||||
'selectedRecipeSubRecipesPositions' => $selectedRecipeSubRecipesPositions,
|
||||
'includedRecipeIdsAbsolute' => $includedRecipeIdsAbsolute,
|
||||
|
@ -31,6 +31,7 @@
|
||||
"tempusdominus-bootstrap-4": "https://github.com/berrnd/tempusdominus-bootstrap-4.git#master",
|
||||
"timeago": "^1.6.7",
|
||||
"toastr": "^2.1.4",
|
||||
"bootstrap-select": "^1.13.10"
|
||||
"bootstrap-select": "^1.13.10",
|
||||
"jquery-lazy": "^1.7.10"
|
||||
}
|
||||
}
|
||||
|
@ -526,3 +526,12 @@ $("textarea.wysiwyg-editor").summernote({
|
||||
minHeight: "300px",
|
||||
lang: __t("summernote_locale")
|
||||
});
|
||||
|
||||
function LoadImagesLazy()
|
||||
{
|
||||
$(".lazy").Lazy({
|
||||
enableThrottle: true,
|
||||
throttle: 500
|
||||
});
|
||||
}
|
||||
LoadImagesLazy();
|
||||
|
@ -82,7 +82,7 @@ Grocy.Components.ProductCard.Refresh = function(productId)
|
||||
{
|
||||
$("#productcard-no-product-picture").addClass("d-none");
|
||||
$("#productcard-product-picture").removeClass("d-none");
|
||||
$("#productcard-product-picture").attr("src", U('/api/files/productpictures/' + btoa(productDetails.product.picture_file_name) + '?force_serve_as=picture&best_fit_height=400&best_fit_width=400'));
|
||||
$("#productcard-product-picture").attr("src", U('/api/files/productpictures/' + btoa(productDetails.product.picture_file_name) + '?force_serve_as=picture&best_fit_width=400'));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -103,12 +103,13 @@ var calendar = $("#calendar").fullCalendar({
|
||||
|
||||
if (recipe.picture_file_name && !recipe.picture_file_name.isEmpty())
|
||||
{
|
||||
element.html(element.html() + '<img src="' + U("/api/files/recipepictures/") + btoa(recipe.picture_file_name) + '?force_serve_as=picture&best_fit_height=400&best_fit_width=400" class="img-fluid">')
|
||||
element.html(element.html() + '<img data-src="' + U("/api/files/recipepictures/") + btoa(recipe.picture_file_name) + '?force_serve_as=picture&best_fit_width=400" class="img-fluid lazy">')
|
||||
}
|
||||
},
|
||||
"eventAfterAllRender": function(view)
|
||||
{
|
||||
RefreshLocaleNumberDisplay();
|
||||
LoadImagesLazy();
|
||||
|
||||
if (GetUriParam("week") !== undefined)
|
||||
{
|
||||
|
@ -33,13 +33,13 @@ class FilesService extends BaseService
|
||||
return $groupFolderPath . '/' . $fileName;
|
||||
}
|
||||
|
||||
public function DownscaleImage($group, $fileName, $bestFitHeight, $bestFitWidth)
|
||||
public function DownscaleImage($group, $fileName, $bestFitHeight = null, $bestFitWidth = null)
|
||||
{
|
||||
$filePath = $this->GetFilePath($group, $fileName);
|
||||
$fileNameWithoutExtension = pathinfo($filePath, PATHINFO_FILENAME);
|
||||
$fileExtension = pathinfo($filePath, PATHINFO_EXTENSION);
|
||||
|
||||
$fileNameDownscaled = $fileNameWithoutExtension . '__downscaledto' . $bestFitHeight . 'x' . $bestFitWidth . '.' . $fileExtension;
|
||||
$fileNameDownscaled = $fileNameWithoutExtension . '__downscaledto' . ($bestFitHeight ? $bestFitHeight : 'auto') . 'x' . ($bestFitWidth ? $bestFitWidth : 'auto') . '.' . $fileExtension;
|
||||
$filePathDownscaled = $this->GetFilePath($group, $fileNameDownscaled);
|
||||
|
||||
try
|
||||
@ -47,7 +47,18 @@ class FilesService extends BaseService
|
||||
if (!file_exists($filePathDownscaled))
|
||||
{
|
||||
$image = new ImageResize($filePath);
|
||||
$image->resizeToBestFit($bestFitHeight, $bestFitWidth);
|
||||
if ($bestFitHeight !== null && $bestFitHeight !== null)
|
||||
{
|
||||
$image->resizeToBestFit($bestFitWidth, $bestFitHeight);
|
||||
}
|
||||
else if ($bestFitHeight !== null)
|
||||
{
|
||||
$image->resizeToHeight($bestFitHeight);
|
||||
}
|
||||
else if ($bestFitWidth !== null)
|
||||
{
|
||||
$image->resizeToWidth($bestFitWidth);
|
||||
}
|
||||
$image->save($filePathDownscaled);
|
||||
}
|
||||
}
|
||||
|
@ -28,7 +28,7 @@
|
||||
<strong>{{ $__t('Spoil rate') }}:</strong> <span id="productcard-product-spoil-rate"></span>
|
||||
|
||||
<h5 class="mt-3">{{ $__t('Product picture') }}</h5>
|
||||
<p class="w-75 mx-auto"><img id="productcard-product-picture" src="" class="img-fluid img-thumbnail d-none"></p>
|
||||
<p class="w-75 mx-auto"><img id="productcard-product-picture" data-src="" class="img-fluid img-thumbnail d-none lazy"></p>
|
||||
<span id="productcard-no-product-picture" class="font-italic d-none">{{ $__t('No picture available') }}</span>
|
||||
|
||||
<h5 class="mt-3">{{ $__t('Price history') }}</h5>
|
||||
|
@ -390,6 +390,7 @@
|
||||
@if(!empty($__t('summernote_locale') && $__t('summernote_locale') != 'x'))<script src="{{ $U('/node_modules', true) }}/summernote/dist/lang/summernote-{{ $__t('summernote_locale') }}.js?v={{ $version }}"></script>@endif
|
||||
<script src="{{ $U('/node_modules/bootstrap-select/dist/js/bootstrap-select.min.js?v=', true) }}{{ $version }}"></script>
|
||||
@if(!empty($__t('bootstrap-select_locale') && $__t('bootstrap-select_locale') != 'x'))<script src="{{ $U('/node_modules', true) }}/bootstrap-select/dist/js/i18n/defaults-{{ $__t('bootstrap-select_locale') }}.js?v={{ $version }}"></script>@endif
|
||||
<script src="{{ $U('/node_modules/jquery-lazy/jquery.lazy.min.js?v=', true) }}{{ $version }}"></script>
|
||||
|
||||
<script src="{{ $U('/js/extensions.js?v=', true) }}{{ $version }}"></script>
|
||||
<script src="{{ $U('/js/grocy.js?v=', true) }}{{ $version }}"></script>
|
||||
|
@ -275,7 +275,7 @@
|
||||
<label class="mt-2">{{ $__t('Picture') }}</label>
|
||||
<button id="delete-current-product-picture-button" class="btn btn-sm btn-danger @if(empty($product->picture_file_name)) disabled @endif"><i class="fas fa-trash"></i> {{ $__t('Delete') }}</button>
|
||||
@if(!empty($product->picture_file_name))
|
||||
<p><img id="current-product-picture" src="{{ $U('/api/files/productpictures/' . base64_encode($product->picture_file_name) . '?force_serve_as=picture&best_fit_height=400&best_fit_width=400') }}" class="img-fluid img-thumbnail mt-2"></p>
|
||||
<p><img id="current-product-picture" data-src="{{ $U('/api/files/productpictures/' . base64_encode($product->picture_file_name) . '?force_serve_as=picture&best_fit_width=400') }}" class="img-fluid img-thumbnail mt-2 lazy"></p>
|
||||
<p id="delete-current-product-picture-on-save-hint" class="form-text text-muted font-italic d-none">{{ $__t('The current picture will be deleted when you save the product') }}</p>
|
||||
@else
|
||||
<p id="no-current-product-picture-hint" class="form-text text-muted font-italic">{{ $__t('No picture available') }}</p>
|
||||
|
@ -209,7 +209,7 @@
|
||||
<label class="mt-2">{{ $__t('Picture') }}</label>
|
||||
<button id="delete-current-recipe-picture-button" class="btn btn-sm btn-danger @if(empty($recipe->picture_file_name)) disabled @endif"><i class="fas fa-trash"></i> {{ $__t('Delete') }}</button>
|
||||
@if(!empty($recipe->picture_file_name))
|
||||
<p><img id="current-recipe-picture" src="{{ $U('/api/files/recipepictures/' . base64_encode($recipe->picture_file_name) . '?force_serve_as=picture&best_fit_height=400&best_fit_width=400') }}" class="img-fluid img-thumbnail mt-2"></p>
|
||||
<p><img id="current-recipe-picture" data-src="{{ $U('/api/files/recipepictures/' . base64_encode($recipe->picture_file_name) . '?force_serve_as=picture&best_fit_width=400') }}" class="img-fluid img-thumbnail mt-2 lazy"></p>
|
||||
<p id="delete-current-recipe-picture-on-save-hint" class="form-text text-muted font-italic d-none">{{ $__t('The current picture will be deleted when you save the recipe') }}</p>
|
||||
@else
|
||||
<p id="no-current-recipe-picture-hint" class="form-text text-muted font-italic">{{ $__t('No picture available') }}</p>
|
||||
|
@ -102,7 +102,7 @@
|
||||
<a class="discrete-link recipe-gallery-item" data-recipe-id="{{ $recipe->id }}" href="#">
|
||||
<div id="recipe-card-{{ $recipe->id }}" class="card border-white mb-0 recipe-card">
|
||||
@if(!empty($recipe->picture_file_name))
|
||||
<img src="{{ $U('/api/files/recipepictures/' . base64_encode($recipe->picture_file_name) . '?force_serve_as=picture&best_fit_height=400&best_fit_width=400') }}" class="img-fluid">
|
||||
<img data-src="{{ $U('/api/files/recipepictures/' . base64_encode($recipe->picture_file_name) . '?force_serve_as=picture&best_fit_width=400') }}" class="img-fluid lazy">
|
||||
@endif
|
||||
<div class="card-body text-center">
|
||||
<h5 class="card-title mb-1">{{ $recipe->name }}</h5>
|
||||
@ -173,7 +173,7 @@
|
||||
</div>
|
||||
|
||||
@if(!empty($selectedRecipeSubRecipe->picture_file_name))
|
||||
<p class="w-75 mx-auto txt-center"><img src="{{ $U('/api/files/recipepictures/' . base64_encode($selectedRecipeSubRecipe->picture_file_name) . '?force_serve_as=picture&best_fit_height=400&best_fit_width=400') }}" class="img-fluid img-thumbnail"></p>
|
||||
<p class="w-75 mx-auto txt-center"><img src="{{ $U('/api/files/recipepictures/' . base64_encode($selectedRecipeSubRecipe->picture_file_name) . '?force_serve_as=picture&best_fit_width=400') }}" class="img-fluid img-thumbnail lazy"></p>
|
||||
@endif
|
||||
|
||||
@php $selectedRecipeSubRecipePositionsFiltered = FindAllObjectsInArrayByPropertyValue($selectedRecipeSubRecipesPositions, 'child_recipe_id', $selectedRecipeSubRecipe->id); @endphp
|
||||
@ -203,7 +203,7 @@
|
||||
@else
|
||||
<span class="locale-number-format" data-format="quantity-amount">@if($selectedRecipePosition->recipe_amount == round($selectedRecipePosition->recipe_amount, 2)){{ round($selectedRecipePosition->recipe_amount, 2) }}@else{{ $selectedRecipePosition->recipe_amount }}@endif</span>
|
||||
@endif
|
||||
{{ $__n($selectedRecipePosition->recipe_amount, FindObjectInArrayByPropertyValue($quantityunits, 'id', $selectedRecipePosition->qu_id)->name, FindObjectInArrayByPropertyValue($quantityunits, 'id', $selectedRecipePosition->qu_id)->name_plural) }} {{ FindObjectInArrayByPropertyValue($products, 'id', $selectedRecipePosition->product_id)->name }}
|
||||
{{ $__n($selectedRecipePosition->recipe_amount, FindObjectInArrayByPropertyValue($quantityUnits, 'id', $selectedRecipePosition->qu_id)->name, FindObjectInArrayByPropertyValue($quantityUnits, 'id', $selectedRecipePosition->qu_id)->name_plural) }} {{ FindObjectInArrayByPropertyValue($products, 'id', $selectedRecipePosition->product_id)->name }}
|
||||
@if($selectedRecipePosition->need_fulfilled == 1)<i class="fas fa-check text-success"></i>@elseif($selectedRecipePosition->need_fulfilled_with_shopping_list == 1)<i class="fas fa-exclamation text-warning"></i>@else<i class="fas fa-times text-danger"></i>@endif
|
||||
<span class="timeago-contextual">@if(FindObjectInArrayByPropertyValue($selectedRecipeSubRecipesPositions, 'recipe_pos_id', $selectedRecipePosition->id)->need_fulfilled == 1) {{ $__t('Enough in stock') }} @else {{ $__t('Not enough in stock, %1$s missing, %2$s already on shopping list', round(FindObjectInArrayByPropertyValue($selectedRecipeSubRecipesPositions, 'recipe_pos_id', $selectedRecipePosition->id)->missing_amount, 2), round(FindObjectInArrayByPropertyValue($selectedRecipeSubRecipesPositions, 'recipe_pos_id', $selectedRecipePosition->id)->amount_on_shopping_list, 2)) }} @endif</span>
|
||||
|
||||
@ -225,7 +225,7 @@
|
||||
|
||||
<!-- Selected recipe -->
|
||||
@if(!empty($selectedRecipe->picture_file_name))
|
||||
<p class="w-75 mx-auto text-center"><img src="{{ $U('/api/files/recipepictures/' . base64_encode($selectedRecipe->picture_file_name) . '?force_serve_as=picture&best_fit_height=400&best_fit_width=400') }}" class="img-fluid img-thumbnail"></p>
|
||||
<p class="w-75 mx-auto text-center"><img src="{{ $U('/api/files/recipepictures/' . base64_encode($selectedRecipe->picture_file_name) . '?force_serve_as=picture&best_fit_width=400') }}" class="img-fluid img-thumbnail lazy"></p>
|
||||
@endif
|
||||
|
||||
@if($selectedRecipePositionsResolved->count() > 0)
|
||||
@ -254,7 +254,7 @@
|
||||
@else
|
||||
<span class="locale-number-format" data-format="quantity-amount">@if($selectedRecipePosition->recipe_amount == round($selectedRecipePosition->recipe_amount, 2)){{ round($selectedRecipePosition->recipe_amount, 2) }}@else{{ $selectedRecipePosition->recipe_amount }}@endif</span>
|
||||
@endif
|
||||
{{ $__n($selectedRecipePosition->recipe_amount, FindObjectInArrayByPropertyValue($quantityunits, 'id', $selectedRecipePosition->qu_id)->name, FindObjectInArrayByPropertyValue($quantityunits, 'id', $selectedRecipePosition->qu_id)->name_plural) }} {{ FindObjectInArrayByPropertyValue($products, 'id', $selectedRecipePosition->product_id)->name }}
|
||||
{{ $__n($selectedRecipePosition->recipe_amount, FindObjectInArrayByPropertyValue($quantityUnits, 'id', $selectedRecipePosition->qu_id)->name, FindObjectInArrayByPropertyValue($quantityUnits, 'id', $selectedRecipePosition->qu_id)->name_plural) }} {{ FindObjectInArrayByPropertyValue($products, 'id', $selectedRecipePosition->product_id)->name }}
|
||||
@if($selectedRecipePosition->need_fulfilled == 1)<i class="fas fa-check text-success"></i>@elseif($selectedRecipePosition->need_fulfilled_with_shopping_list == 1)<i class="fas fa-exclamation text-warning"></i>@else<i class="fas fa-times text-danger"></i>@endif
|
||||
<span class="timeago-contextual">@if(FindObjectInArrayByPropertyValue($recipePositionsResolved, 'recipe_pos_id', $selectedRecipePosition->id)->need_fulfilled == 1) {{ $__t('Enough in stock') }} @else {{ $__t('Not enough in stock, %1$s missing, %2$s already on shopping list', round(FindObjectInArrayByPropertyValue($recipePositionsResolved, 'recipe_pos_id', $selectedRecipePosition->id)->missing_amount, 2), round(FindObjectInArrayByPropertyValue($recipePositionsResolved, 'recipe_pos_id', $selectedRecipePosition->id)->amount_on_shopping_list, 2)) }} @endif</span>
|
||||
|
||||
|
@ -211,6 +211,13 @@ gettext-translator@^2.1.0:
|
||||
dependencies:
|
||||
sprintf-js "^1.0.3"
|
||||
|
||||
jquery-lazy@^1.7.10:
|
||||
version "1.7.10"
|
||||
resolved "https://registry.yarnpkg.com/jquery-lazy/-/jquery-lazy-1.7.10.tgz#aa3d43d058bf1ea89284214f4521f6d9a162d051"
|
||||
integrity sha512-1vBpU+igfZJ58BrQrk2OFl3TMriGeIfZUg35uSIajZF4GcxF96NtdS3LbWk5a72rH2ypz0odTsAkqhUsICknhw==
|
||||
dependencies:
|
||||
jquery ">=1.7.2"
|
||||
|
||||
jquery-serializejson@^2.9.0:
|
||||
version "2.9.0"
|
||||
resolved "https://registry.yarnpkg.com/jquery-serializejson/-/jquery-serializejson-2.9.0.tgz#03e3764e3a4b42c1c5aae9f93d7f19320c5f35a6"
|
||||
@ -236,7 +243,7 @@ jquery@3.3.1:
|
||||
resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.3.1.tgz#958ce29e81c9790f31be7792df5d4d95fc57fbca"
|
||||
integrity sha512-Ubldcmxp5np52/ENotGxlLe6aGMvmF4R8S6tZjsP6Knsaxd/xp3Zrh50cG93lR6nPXyUFwzN3ZSOQI0wRJNdGg==
|
||||
|
||||
jquery@>=1.12.0, "jquery@>=1.5.0 <4.0", jquery@>=1.7, jquery@>=1.9.1, jquery@^3.0, jquery@^3.4.1:
|
||||
jquery@>=1.12.0, "jquery@>=1.5.0 <4.0", jquery@>=1.7, jquery@>=1.7.2, jquery@>=1.9.1, jquery@^3.0, jquery@^3.4.1:
|
||||
version "3.4.1"
|
||||
resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.4.1.tgz#714f1f8d9dde4bdfa55764ba37ef214630d80ef2"
|
||||
integrity sha512-36+AdBzCL+y6qjw5Tx7HgzeGCzC81MDDgaUP8ld2zhx58HdqXGoBd+tHdrBMiyjGQs0Hxs/MLZTu/eHNJJuWPw==
|
||||
|
Loading…
x
Reference in New Issue
Block a user