我正在尝试使用this masonry js package显示图像,但是遇到了困难。
我有一张图片上传表格。用户单击按钮将图像绑定到Model.Images
,然后将它们显示在页面上。用户可以在提交表单之前绑定多批图像。每次用户将另一批图像绑定到Model.Images
时,前一批图像都会保留下来-也就是说,在绑定下一批图像之前,我不会截断Model.Images
。
我看到的问题是:每次添加Model.Images
时,都需要重新触发砌石js脚本,因为js仅适用于最近渲染的视图。但是,似乎每次我强制StateHasChanged();
中最近添加的批次中的图像都与以前的图像重叠时,所以我看到了这一点:
我认为这与差异渲染有关。如果我将图像移动到辅助对象,则截断Model.Images
,强制StateHasChanged()
,然后重新填充Model.Images
,图像将正确并排显示。我很可能只是错误地解决了这个问题。有人可以建议我如何在没有恶意实现的情况下实现我想要的吗?我在下面粘贴了我的代码的简化版本-该实现产生了上图所示的不良结果。
剃刀组件
(将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
的工作方式有关。我按照您的建议调整了数组的大小,但我只是创建一个填充有空引用的适当长度的数组。我意识到这肯定是错的,但是我不确定用什么填充它们-我尝试用object[] imageRefs
替换ElementReference[] imageRefs
并用imageRefs
填充ElementReference[i].Id
并将其传递给JS函数,但并没有改变页面的整体行为。为了简化代码,我恢复了基准。
我在一个不同的示例下粘贴,在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与原始帖子中的相同。
答案 0 :(得分:2)
您正在循环捕获参考。这只会分配最终循环参考。一旦知道有多少次迭代,就必须调整数组大小以捕获引用。 refs = new object[someValue]
然后,您的触发器砌体功能将必须遍历参考列表。
<div @key="@Model.Images[iCopy]" class="grid-item" @ref=refs[iCopy]>
<div class="card m-2 shadow-sm" style="position:relative;">
<img src="@uri" class="card-img-top" alt="..." loading="lazy">
</div>
</div>
@code {
object[] refs;
}
在渲染完每个引用后,调用Java脚本。
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
foreach (var obj in refs)
{
await JSRuntime.InvokeVoidAsync("masonryAppendDelay", obj);
}
}
}