利用a-menu和scrollIntoView()实现菜单式锚点
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了利用a-menu和scrollIntoView()实现菜单式锚点相关的知识,希望对你有一定的参考价值。
参考技术A 本来想用ant-design自带的anchor组件实现锚点,但是这样会在切换锚点时改变页面的URL,有点麻烦,所以用menu组件和 scrollIntoView() 实现了一个目录样式的锚点。分为以下几步:
1.利用menu组件作为锚点来画图
2.点击menu-item时利用 scrollIntoView() 实现定位的切换
3.给内容所在div绑定scroll监听函数,使滚动到指定位置时,menu也选择到相应的menu-item
scrollIntoView介绍
ps:elementHeight数组中存放的元素高度,一开始在mounted中计算,但是却获取不到,这是因为 mounted不会保证所有的子组件也都被挂载完成 ,可以使用 this.$nextTick() 来解决这个问题。如果仍未能解决,可能是因为部分元素在更后的生命周期中发生了新的变化,可以使用 setTimeout() 来解决。
ScrollIntoView 和 ListView 与虚拟化
【中文标题】ScrollIntoView 和 ListView 与虚拟化【英文标题】:ScrollIntoView and ListView with virtualization 【发布时间】:2016-07-11 09:14:11 【问题描述】:我有ListView
(虚拟化默认开启),ItemsSource
绑定到ObservableCollection<Item>
属性。
填充数据时(设置属性并发出通知)我在分析器中看到 2 个布局峰值,第二个发生在调用 listView.ScrollIntoView()
之后。
我的理解是:
ListView
通过绑定加载数据并为屏幕上的项目创建ListViewItem
,从索引 0 开始。
然后我打电话给listView.ScrollIntoView()
。
现在 ListView
第二次执行此操作(创建 ListViewItem
s)。
如何防止这种去虚拟化发生两次(我不希望在ScrollIntoView
之前发生一次)?
我尝试使用ListBox
进行复制。
xaml:
<Grid>
<ListBox x:Name="listBox" ItemsSource="Binding Items">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="IsSelected" Value="Binding IsSelected" />
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
<Button Content="Fill" VerticalAlignment="Top" HorizontalAlignment="Center" Click="Button_Click" />
</Grid>
cs:
public class NotifyPropertyChanged : INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string property = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
public class ViewModel : NotifyPropertyChanged
public class Item : NotifyPropertyChanged
bool _isSelected;
public bool IsSelected
get return _isSelected;
set
_isSelected = value;
OnPropertyChanged();
ObservableCollection<Item> _items = new ObservableCollection<Item>();
public ObservableCollection<Item> Items
get return _items;
set
_items = value;
OnPropertyChanged();
public partial class MainWindow : Window
ViewModel _vm = new ViewModel();
public MainWindow()
InitializeComponent();
DataContext = _vm;
void Button_Click(object sender, RoutedEventArgs e)
var list = new List<ViewModel.Item>(1234567);
for (int i = 0; i < 1234567; i++)
list.Add(new ViewModel.Item());
list.Last().IsSelected = true;
_vm.Items = new ObservableCollection<ViewModel.Item>(list);
listBox.ScrollIntoView(list.Last());
Debug - Performance Profiler - Application Timeline... 稍等,单击按钮,稍等,关闭窗口。您将看到 2 个带有 VirtualizingStackPanel
的布局通道。我的目标是只有一个,但我不知道怎么做。
repro的问题是模拟负载(创建ListViewItem
is expensive时),但我希望现在能更清楚地证明问题。
【问题讨论】:
您是否总是希望在将项目集合添加到 UI 后显示最后一个项目?它不能解决两个布局传递的问题,但如果是这种情况,您可能会在将项目添加到 UI 之前更改项目的排序方式,以便您想要显示的项目是集合中的第一个项目 @Bijington,集合已经排序(按日期,未显示在 mcve 中),我不应该更改顺序。它可以是任何项目(在实际项目中它是多选ListView
,滚动到最后选择的项目)。理想情况下,我正在寻找在 MVVM 应用程序中存储/恢复 ListView
状态的方法(选择已处理,但滚动位置未处理,它以某种方式由 ScrollIntoView
处理),即双重去虚拟化是一种 XY 问题(但我对 ScrollIntoView
没意见,问题只有两次去虚拟化)。
我假设了很多,但我认为值得一问。这听起来确实是一个有趣的问题。在以某种方式加载其内容之前,您需要告诉ListView
它的滚动位置。您能否以某种方式子类化 ListView
并阻止渲染,直到您完成加载传递(可能是 IsLoadingContent 标志),以便您可以分配项目并标记需要选择并显示哪些项目?
如果将ListBox的可见性设置为隐藏,然后在调用ScrollIntoView后使其可见,会解决问题吗?
【参考方案1】:
滚动方法在VirtualizingStackPanel
上通常不能很好地工作。为了解决这个问题,我使用以下解决方案。
-
放弃
VirtualizingStackPanel
。使用普通的 StackPanel 作为面板模板。
从这里将 DataTemplate 的外层设为 LazyControl:http://blog.angeloflogic.com/2014/08/lazycontrol-in-junglecontrols.html
确保在 LazyControl 上设置高度。
我通常会从这种方法中获得良好的性能。要使其完全按照您的要求进行操作,您可能需要向 LazyControl 添加一些额外的逻辑以等待设置某些标志(在您调用滚动方法之后)。
【讨论】:
以上是关于利用a-menu和scrollIntoView()实现菜单式锚点的主要内容,如果未能解决你的问题,请参考以下文章
window.scrollto 和 scrollIntoView 在 ios 中不能平滑滚动
antd(ant design) 多级菜单时 subMenu无法展开的问题