利用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 第二次执行此操作(创建 ListViewItems)。

如何防止这种去虚拟化发生两次(我不希望在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的问题是模拟负载(创建ListViewItemis 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无法展开的问题

类型错误:scrollIntoView 不是函数

scrollIntoView behavior smooth not work

在 scrollIntoView 的动态长度数组中使用 Refs