【发布时间】:2021-02-12 00:16:44
【问题描述】:
我正在尝试使用 this masonry js package 显示图像,但我遇到了困难。
我有一个图片上传表单。用户单击按钮将图像绑定到Model.Images,然后它们会显示在页面上。用户可以在提交表单之前绑定多批图片。每次用户将另一批图像绑定到Model.Images 时,都会保留上一批图像——也就是说,在绑定下一批图像之前,我不会截断Model.Images。
我看到的问题是:每次添加 Model.Images 时都需要重新触发 masonry js 脚本,因为 js 仅适用于最近渲染的视图。但是,似乎每次我强制StateHasChanged(); 来自最近添加的批次的图像都会与以前的图像重叠,所以我看到了这个:
我认为这与差分渲染有关。如果我将图像移动到辅助对象,截断Model.Images,强制StateHasChanged(),然后重新填充Model.Images,图像将正确并排显示。我很可能只是错误地处理了这个问题。有人可以建议我如何在没有我的 hacky 实现的情况下实现我想要的吗?我在下面粘贴了我的代码的简化版本 - 这是产生上图所示不良结果的实现。
剃须刀组件
(TriggerMasonry() 事件在 Model.Images 被附加到之后被调用。)
@for (int i = 0; i < Model.Images.Count; j++)
{
var iCopy = i; //see https://stackoverflow.com/a/56426146/323447
var uri = $"data:{Model.Images[iCopy].ImageMimeType};base64,{Convert.ToBase64String(Model.Images[iCopy].ImageDataLarge.ToArray())}";
<div class="grid-item" @ref=MasonryElement>
<div class="card m-2 shadow-sm" style="position:relative;">
<img src="@uri" class="card-img-top" alt="..." loading="lazy">
</div>
</div>
}
@code {
ElementReference MasonryElement;
private async void TriggerMasonry()
{
if (Model.Images.Where(x => x.ImageData != null).Any())
{
StateHasChanged();
await JSRuntime.InvokeVoidAsync("initMasonryDelay", MasonryElement);
StateHasChanged();
}
}
}
js
function initMasonryDelay() {
setTimeout(function () {
// init Masonry
var $grid = $('.grid').masonry({
itemSelector: '.grid-item',
percentPosition: true,
columnWidth: '.grid-sizer'
});
// layout Masonry after each image loads
$grid.imagesLoaded().progress(function () {
$grid.masonry();
});
}, 150); //milliseconds
}
css
* {
box-sizing: border-box;
}
.grid:after {
content: '';
display: block;
clear: both;
}
.grid-sizer,
.grid-item {
width: 33.333%;
}
@media (max-width: 575px) {
.grid-sizer,
.grid-item {
width: 100%;
}
}
@media (min-width: 576px) and (max-width: 767px) {
.grid-sizer,
.grid-item {
width: 50%;
}
}
/* To change the amount of columns on larger devices, uncomment the code below */
@media (min-width: 768px) and (max-width: 991px) {
.grid-sizer,
.grid-item {
width: 33.333%;
}
}
@media (min-width: 992px) and (max-width: 1199px) {
.grid-sizer,
.grid-item {
width: 25%;
}
}
@media (min-width: 1200px) {
.grid-sizer,
.grid-item {
width: 20%;
}
}
.grid-item {
float: left;
}
.grid-item img {
display: block;
max-width: 100%;
}
----用户@Brian_Parker的另一个例子----
我试图应用你的建议,但我仍然没有任何运气。我显然不能理解 JS 互操作性如何与 blazor 组件一起工作。我认为我的问题与不了解 @ref 的工作原理有关。我按照您的建议调整了数组的大小,但我只是创建了一个用空引用填充的适当长度的数组。我意识到这一定是错误的,但我不确定用什么填充它们 - 我尝试用 ElementReference[] imageRefs 替换 object[] imageRefs 并用 imageRefs 填充 ElementReference[i].Id 并将其传递给 JS 函数,但它没有t 改变页面的整体行为。为了让我的代码更容易消化,我恢复到基线。
我在下面粘贴了一个不同的示例,在 UI 上下文中可能更容易理解。如果您能帮助我理解我在这里做错了什么,我将不胜感激:
我有另一个组件,我正在尝试将 Masonry JS 应用于动态生成的图像列表。基本概述:加载时,从服务器拉出 10 张图像并进行渲染。当用户单击“加载更多”按钮时,会从服务器中提取另外 10 个图像并进行渲染。我看到了第二轮图像覆盖第一轮的相同问题。
我的剃须刀组件:
<!-- Masonry -->
<div class="grid">
<div class="grid-sizer"></div>
@for (int i = 0; i < pagedThings.Data.Count; i++)
{
var iCopy = i; //see https://stackoverflow.com/a/56426146/323447
<div @key="@pagedThings.Data[iCopy]" class="grid-item" @ref=imageRefs[iCopy]>
<div class="card">
@{
var mimeType = pagedThings.Data[iCopy].ImageThings.FirstOrDefault().ImageMimeTypeLarge;
var imgData = pagedThings.Data[iCopy].ImageThings.FirstOrDefault().ImageDataLarge;
}
<img src="@String.Format("data:" + mimeType + ";base64,{0}", Convert.ToBase64String(imgData))" loading="lazy">
</div>
</div>
}
</div>
<!-- Load more button -->
<button class="btn" type="button" @onclick="GetMoreThings">
<span>Load more</span>
</button>
@code {
PagedResponse<List<Thing>> pagedThings { get; set; }
private int currentPage = 1;
private int currentPageSize = 10;
object[] imageRefs;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
//load Things
await LoadThings();
}
await base.OnAfterRenderAsync(firstRender);
}
private async Task LoadThings()
{
pagedThings = await ThingService.GetByPageAsync($"api/Things/GetThingsByPage?pageNumber={currentPage}&pageSize={currentPageSize}");
imageRefs = new object[pagedThings.Data.Count()];
await JSRuntime.InvokeVoidAsync("masonryAppendDelay", imageRefs[imageRefs.Length - 1]);
StateHasChanged();
}
private async Task GetMoreThings()
{
currentPage++;
var newData = await ThingService.GetByPageAsync($"api/Things/GetThingsByPage?pageNumber={currentPage}&pageSize={currentPageSize}");
foreach (var item in newData.Data)
{
pagedThings.Data.Add(item);
}
imageRefs = new object[pagedThings.Data.Count()];
await JSRuntime.InvokeVoidAsync("masonryAppendDelay", imageRefs[imageRefs.Length - 1]);
StateHasChanged();
}
}
我的 JS:
function masonryAppendDelay(element) {
setTimeout(function () {
// init Masonry
var $grid = $('.grid').masonry({
itemSelector: element,
percentPosition: true,
columnWidth: '.grid-sizer'
});
// layout Masonry after each image loads
$grid.imagesLoaded().progress(function () {
$grid.masonry();
//$grid.masonry('layout');
});
}, 200); //milliseconds
}
我的 css 和原帖中的一样。
【问题讨论】:
-
您正在调整 imageRefs = new object[pagedThings.Data.Count()];但没有用任何数据填充它。我也不了解 imageRefs 的应用方式。
-
嘿@sion_corn 您是否对使用 masonry js 的完整 blazor 实现有一个概述? (我的意思是一个演示网站)我很乐意使用它。
标签: javascript c# asp.net-core blazor