mirror of
https://gitee.com/LongbowEnterprise/BootstrapBlazor.git
synced 2024-11-30 02:58:37 +08:00
feat(IntersectionObserver): add IntersectionObserver component (#3984)
* doc: 增加源代码映射关系 * doc: 增加交叉检测组件 * feat: 增加交叉检测组件 * doc: 增加示例 * refactor: 增加示例 * refactor: 增加脚本 * refactor: 更新交叉检测逻辑 * doc: 更新示例 * doc: 更新文档本地化
This commit is contained in:
parent
a7f9747346
commit
476c935645
@ -0,0 +1,20 @@
|
||||
@page "/intersection-observer"
|
||||
@inject IStringLocalizer<IntersectionObservers> Localizer
|
||||
|
||||
<h3>@Localizer["IntersectionObserverTitle"]</h3>
|
||||
|
||||
<h4>@Localizer["IntersectionObserverDescription"]</h4>
|
||||
|
||||
<DemoBlock Title="@Localizer["IntersectionObserverBaseUsage"]"
|
||||
Introduction="@Localizer["IntersectionObserverNormalIntro"]"
|
||||
Name="Normal">
|
||||
<section ignore>@((MarkupString)Localizer["IntersectionObserverNormalDescription"].Value)</section>
|
||||
<IntersectionObserver class="bb-list-main scroll" OnIntersectingAsync="OnIntersectingAsync">
|
||||
@foreach (var image in _images)
|
||||
{
|
||||
<IntersectionObserverItem class="bb-list-item">
|
||||
<img src="@image" />
|
||||
</IntersectionObserverItem>
|
||||
})
|
||||
</IntersectionObserver>
|
||||
</DemoBlock>
|
@ -0,0 +1,32 @@
|
||||
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
// Website: https://www.blazor.zone or https://argozhang.github.io/
|
||||
|
||||
namespace BootstrapBlazor.Server.Components.Samples;
|
||||
|
||||
/// <summary>
|
||||
/// 交叉检测组件示例
|
||||
/// </summary>
|
||||
public partial class IntersectionObservers
|
||||
{
|
||||
private List<string> _images = default!;
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
base.OnInitialized();
|
||||
|
||||
_images = Enumerable.Range(1, 100).Select(i => "../../images/default.jpeg").ToList();
|
||||
}
|
||||
|
||||
private Task OnIntersectingAsync(int index)
|
||||
{
|
||||
_images[index] = GetImageUrl(index);
|
||||
StateHasChanged();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private static string GetImageUrl(int index) => $"https://picsum.photos/160/160?random={index}";
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
::deep + .bb-list-main {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
height: 500px;
|
||||
padding: .25rem;
|
||||
border: var(--bs-border-width) solid var(--bs-border-color);
|
||||
border-radius: var(--bs-border-radius);
|
||||
}
|
||||
|
||||
::deep + .bb-list-main .bb-list-item {
|
||||
width: 160px;
|
||||
height: 160px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: .25rem;
|
||||
}
|
||||
|
||||
::deep + .bb-list-main .bb-list-item img {
|
||||
width: 160px;
|
||||
object-fit: cover;
|
||||
}
|
@ -1171,6 +1171,12 @@ internal static class MenusLocalizerExtensions
|
||||
Url = "light"
|
||||
},
|
||||
new()
|
||||
{
|
||||
IsNew = true,
|
||||
Text = Localizer["IntersectionObserver"],
|
||||
Url = "intersection-observer"
|
||||
},
|
||||
new()
|
||||
{
|
||||
IsNew = true,
|
||||
Text = Localizer["Marquee"],
|
||||
|
@ -4702,7 +4702,8 @@
|
||||
"ConnectionService": "ConnectionService",
|
||||
"ExportPdfButton": "ExportPdfButton",
|
||||
"ThemeProvider": "IThemeProvider",
|
||||
"IconPark": "ByteDance IconPark"
|
||||
"IconPark": "ByteDance IconPark",
|
||||
"IntersectionObserver": "IntersectionObserver"
|
||||
},
|
||||
"BootstrapBlazor.Server.Components.Samples.Table.TablesHeader": {
|
||||
"TablesHeaderTitle": "Header grouping function",
|
||||
@ -6587,5 +6588,12 @@
|
||||
"DriverJsDestroyTitle": "Destroy callback",
|
||||
"DriverJsDestroyIntro": "Callback method before destruction <code>OnBeforeDestroyAsync</code> or callback method for destruction <code>OnDestroyedAsync</code>",
|
||||
"DriverJsDestroyDesc": "You can use the <code>OnBeforeDestroyAsync</code> callback to add some logic when the user tries to exit the tour. Prevent destruction when the callback method <code>OnBeforeDestroyAsync</code> returns not <code>NULL</code> string. You can also prevent the user from exiting the tour using <code>AllowClose</code> option. This option is useful when you want to force the user to complete the tour before they can exit."
|
||||
},
|
||||
"BootstrapBlazor.Server.Components.Samples.IntersectionObservers": {
|
||||
"IntersectionObserverTitle": "IntersectionObserver",
|
||||
"IntersectionObserverDescription": "Scrolling makes the element visible to trigger component callbacks, mostly used for data lazy loading functions",
|
||||
"IntersectionObserverBaseUsage": "Basic usage",
|
||||
"IntersectionObserverNormalIntro": "Monitor element visibility changes by setting <code>RootSelector</code> <code>RootMargin</code> <code>Threshold</code> parameter values",
|
||||
"IntersectionObserverNormalDescription": "This example loads 100 pictures. The invisible pictures load the default pictures (cached). When you scroll to the visible area, you load the real pictures."
|
||||
}
|
||||
}
|
||||
|
@ -4702,7 +4702,8 @@
|
||||
"ConnectionService": "在线连接服务 ConnectionService",
|
||||
"ExportPdfButton": "导出 Pdf 按钮 ExportPdfButton",
|
||||
"ThemeProvider": "主题服务 IThemeProvider",
|
||||
"IconPark": "字节跳动图标 IconPark"
|
||||
"IconPark": "字节跳动图标 IconPark",
|
||||
"IntersectionObserver": "交叉观察者 IntersectionObserver"
|
||||
},
|
||||
"BootstrapBlazor.Server.Components.Samples.Table.TablesHeader": {
|
||||
"TablesHeaderTitle": "表头分组功能",
|
||||
@ -6587,5 +6588,12 @@
|
||||
"DriverJsDestroyTitle": "销毁回调方法",
|
||||
"DriverJsDestroyIntro": "销毁前回调方法 <code>OnBeforeDestroyAsync</code> 或者销毁回调方法 <code>OnDestroyedAsync</code>",
|
||||
"DriverJsDestroyDesc": "当用户尝试退出游览时,可以使用 <code>OnBeforeDestroyAsync</code> 回调添加销毁前逻辑,返回 <b>非空字符串</b> 时客户端弹窗二次确认是否阻止销毁;可通过设置 <code>AllowClose</code> 阻止用户退出向导"
|
||||
},
|
||||
"BootstrapBlazor.Server.Components.Samples.IntersectionObservers": {
|
||||
"IntersectionObserverTitle": "IntersectionObserver 可见性观察器",
|
||||
"IntersectionObserverDescription": "通过滚动使元素可见使触发组件回调,多用于数据懒加载功能",
|
||||
"IntersectionObserverBaseUsage": "基础用法",
|
||||
"IntersectionObserverNormalIntro": "通过设置 <code>Root</code> <code>RootMargin</code> <code>Threshold</code> 参数值,监听元素可见性变化",
|
||||
"IntersectionObserverNormalDescription": "本例加载 100 张图片,不可见图片加载默认图片(已缓存),当滚动到可见区域时,加载真实图片"
|
||||
}
|
||||
}
|
||||
|
@ -106,6 +106,7 @@
|
||||
"input": "Inputs",
|
||||
"input-group": "InputGroups",
|
||||
"ip": "Ips",
|
||||
"intersection-observer": "IntersectionObservers",
|
||||
"mask": "Masks",
|
||||
"markdown": "Markdowns",
|
||||
"marquee": "Marquees",
|
||||
|
BIN
src/BootstrapBlazor.Server/wwwroot/images/default.jpeg
Normal file
BIN
src/BootstrapBlazor.Server/wwwroot/images/default.jpeg
Normal file
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
@ -0,0 +1,7 @@
|
||||
@namespace BootstrapBlazor.Components
|
||||
@inherits BootstrapModuleComponentBase
|
||||
@attribute [BootstrapModuleAutoLoader(JSObjectReference = true)]
|
||||
|
||||
<div @attributes="AdditionalAttributes" id="@Id" class="@ClassString">
|
||||
@ChildContent
|
||||
</div>
|
@ -0,0 +1,84 @@
|
||||
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
// Website: https://www.blazor.zone or https://argozhang.github.io/
|
||||
|
||||
|
||||
namespace BootstrapBlazor.Components;
|
||||
|
||||
/// <summary>
|
||||
/// 交叉检测组件
|
||||
/// </summary>
|
||||
public partial class IntersectionObserver
|
||||
{
|
||||
/// <summary>
|
||||
/// The element that is used as the viewport for checking visibility of the target. Must be the ancestor of the target. Defaults to the browser viewport if not specified or if null
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public string? RootSelector { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Margin around the root. Can have values similar to the CSS margin property, e.g. "10px 20px 30px 40px" (top, right, bottom, left). The values can be percentages. This set of values serves to grow or shrink each side of the root element's bounding box before computing intersections. Defaults to all zeros.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public string? RootMargin { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Either a single number or an array of numbers which indicate at what percentage of the target's visibility the observer's callback should be executed. If you only want to detect when visibility passes the 50% mark, you can use a value of 0.5. If you want the callback to run every time visibility passes another 25%, you would specify the array [0, 0.25, 0.5, 0.75, 1]. The default is 0 (meaning as soon as even one pixel is visible, the callback will be run). A value of 1.0 means that the threshold isn't considered passed until every pixel is visible.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public float Threshold { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获得/设置 已经交叉回调方法
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public Func<int, Task>? OnIntersectingAsync { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获得/设置 子组件
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public RenderFragment? ChildContent { get; set; }
|
||||
|
||||
private string? ClassString => CssBuilder.Default("bb-intersection-observer")
|
||||
.AddClassFromAttributes(AdditionalAttributes)
|
||||
.Build();
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
base.OnParametersSet();
|
||||
|
||||
if (Threshold < 0 || Threshold > 1)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(Threshold), $"{nameof(Threshold)} must be between 0 and 1");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(RootMargin))
|
||||
{
|
||||
RootMargin = "0px 0px 0px 0px";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected override Task InvokeInitAsync() => InvokeVoidAsync("init", Id, Interop, new { Root = RootSelector, RootMargin, Threshold });
|
||||
|
||||
/// <summary>
|
||||
/// 交叉检测回调方法 由 JavaScript 调用
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
/// <returns></returns>
|
||||
[JSInvokable]
|
||||
public async Task OnIntersecting(int index)
|
||||
{
|
||||
if (OnIntersectingAsync != null)
|
||||
{
|
||||
await OnIntersectingAsync(index);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
import Data from "../../modules/data.js"
|
||||
|
||||
export function init(id, invoke, options) {
|
||||
console.log(options);
|
||||
|
||||
const el = document.getElementById(id);
|
||||
const items = [...el.querySelectorAll(".bb-intersection-observer-item")];
|
||||
const observer = new IntersectionObserver(entries => {
|
||||
entries.forEach(entry => {
|
||||
if (entry.isIntersecting) {
|
||||
observer.unobserve(entry.target);
|
||||
const index = items.indexOf(entry.target);
|
||||
invoke.invokeMethodAsync('OnIntersecting', index);
|
||||
};
|
||||
});
|
||||
}, options);
|
||||
|
||||
items.forEach(item => observer.observe(item));
|
||||
Data.set(id, observer);
|
||||
}
|
||||
|
||||
export function dispose(id) {
|
||||
const observer = Data.get(id);
|
||||
Data.remove(id);
|
||||
|
||||
if (observer) {
|
||||
observer.disconnect();
|
||||
observer = null;
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
@namespace BootstrapBlazor.Components
|
||||
@inherits BootstrapModuleComponentBase
|
||||
|
||||
<div @attributes="AdditionalAttributes" class="@ClassString">
|
||||
@ChildContent
|
||||
</div>
|
@ -0,0 +1,21 @@
|
||||
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
// Website: https://www.blazor.zone or https://argozhang.github.io/
|
||||
|
||||
namespace BootstrapBlazor.Components;
|
||||
|
||||
/// <summary>
|
||||
/// 检测交叉组件子组件
|
||||
/// </summary>
|
||||
public partial class IntersectionObserverItem
|
||||
{
|
||||
/// <summary>
|
||||
/// 获得/设置 子组件
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public RenderFragment? ChildContent { get; set; }
|
||||
|
||||
private string? ClassString => CssBuilder.Default("bb-intersection-observer-item")
|
||||
.AddClassFromAttributes(AdditionalAttributes)
|
||||
.Build();
|
||||
}
|
Loading…
Reference in New Issue
Block a user