封装了一个中间放大效果的iOS轮播视图

news2024/11/24 1:53:14

效果图

请添加图片描述

计算逻辑

设定在中间展示的size,即正常size,然后设置水平和竖直方向上的margin, 在view的origin和scrollView的contentoffset相等的时候,即
视图处在正中间的时候,最大,然后通过计算其他视图的origin和scrollView.contentoffset 的距离设置大小,和contentoffset这个位置的frame的距离为一个视图宽度的时候,达到最小size,即最大size减去margin。然后通过计算该视图的origin和 contentffset的差的绝对值,即距离 占 视图宽度的比例,计算需要缩小的size

核心代码

//
//  LBHorizontalLoopView.m
//  LBHorizontalLoopView
//
//  Created by liubo on 2021/8/29.
//

#import "LBMiddleExpandLoopView.h"

@interface LBMiddleExpandLoopView ()<UIScrollViewDelegate>

@property (nonatomic, strong, readwrite) LBMiddleExpandLoopViewBaseCell *currentCell;

@property (nonatomic, assign, readwrite) NSInteger currentSelectIndex;

// 实际的个数
@property (nonatomic, assign) NSInteger realCount;

// 显示的个数
@property (nonatomic, assign) NSInteger showCount;

// 定时器
@property (nonatomic, weak) NSTimer     *timer;
@property (nonatomic, assign) NSInteger timerIndex;

// 当前显示的cell大小
@property (nonatomic, assign) CGSize cellSize;

@property (nonatomic, strong) NSMutableDictionary *viewClsDict;
@property (nonatomic, strong) NSMutableArray *visibleCells;
@property (nonatomic, strong) NSMutableArray *reusableCells;
@property (nonatomic, assign) NSRange        visibleRange;

// 处理xib加载时导致的尺寸不准确问题
@property (nonatomic, assign) CGSize        originSize;

@end

@implementation LBMiddleExpandLoopView

#pragma mark - Life Cycle
- (instancetype)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        [self initialization];
    }
    return self;
}

- (instancetype)initWithCoder:(NSCoder *)aDecoder {
    if (self = [super initWithCoder:aDecoder]) {
        [self initialization];
    }
    return self;
}

- (void)layoutSubviews {
    [super layoutSubviews];
    
    if (CGSizeEqualToSize(self.originSize, CGSizeZero)) return;
    
    // 解决xib加载时导致的布局错误问题
    if (!CGSizeEqualToSize(self.bounds.size, self.originSize)) {
        [self updateScrollViewAndCellSize];
    }
}

// 解决当父视图释放时,当前视图因为NSTimer强引用而导致的不能释放
- (void)willMoveToSuperview:(UIView *)newSuperview {
    if (!newSuperview) {
        [self stopTimer];
    }
}

- (void)dealloc {
    [self stopTimer];
    self.scrollView.delegate = nil;
}

// 重新此方法是为了解决当cell超出UIScrollView时不能点击的问题
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    if ([self pointInside:point withEvent:event]) {
        // 判断点击的点是否在cell上
        for (UIView *cell in self.scrollView.subviews) {
            // 将cell的frame转换到当前视图上
            CGRect convertFrame = CGRectZero;
            convertFrame.size = cell.frame.size;
            
            convertFrame.origin.x = cell.frame.origin.x + self.scrollView.frame.origin.x - self.scrollView.contentOffset.x;
            convertFrame.origin.y = self.scrollView.frame.origin.y + cell.frame.origin.y;
            
            // 判断点击的点是否在cell上
            if (CGRectContainsPoint(convertFrame, point)) {
                // 修复cell上添加其他点击事件无效的bug
                UIView *view = [super hitTest:point withEvent:event];
                if (view == self || view == cell || view == self.scrollView) return cell;
                return view;
            }
        }
        // 判断点击的点是否在UIScrollView上
        CGPoint newPoint = CGPointZero;
        newPoint.x = point.x - self.scrollView.frame.origin.x + self.scrollView.contentOffset.x;
        newPoint.y = point.y - self.scrollView.frame.origin.y + self.scrollView.contentOffset.y;
        if ([self.scrollView pointInside:newPoint withEvent:event]) {
            return [self.scrollView hitTest:newPoint withEvent:event];
        }
        // 系统处理
        return [super hitTest:point withEvent:event];
    }
    return nil;
}

#pragma mark - Public Methods
- (void)reloadData {
    // 移除所有cell
    [self.scrollView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
    
    // 停止定时器
    [self stopTimer];
    
    // 加载数据
    if (self.dataSource && [self.dataSource respondsToSelector:@selector(numberOfCellsInCycleScrollView:)]) {
        // 实际个数
        self.realCount = [self.dataSource numberOfCellsInCycleScrollView:self];
        
        // 展示个数
        if (self.isInfiniteLoop) {
            self.showCount = self.realCount == 1 ? 1 : self.realCount * 3;
        }else {
            self.showCount = self.realCount;
        }
    }
    
    // 清除原始数据
    [self.visibleCells removeAllObjects];
    [self.reusableCells removeAllObjects];
    self.visibleRange = NSMakeRange(0, 0);
    
    //cell数量为1或defaultSelectIndex超过了当前数量
    if(self.defaultSelectIndex >= self.realCount) {
        self.defaultSelectIndex = 0;
    }
    if(self.realCount == 1) {
        self.timerIndex = 0;
    }
    
    for (NSInteger i = 0; i < self.showCount; i++){
        [self.visibleCells addObject:[NSNull null]];
    }
    
    __weak __typeof(self) weakSelf = self;
    [self refreshSizeCompletion:^{
        [weakSelf initialScrollViewAndCellSize];
    }];
}

- (void)refreshSizeCompletion:(void(^)(void))completion {
    if (self.bounds.size.width == 0 || self.bounds.size.height == 0) {
        [self layoutIfNeeded];
        // 此处做延时处理是为了解决使用Masonry布局时导致的view的大小不能及时更新的bug
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            if (self.bounds.size.width == 0 || self.bounds.size.height == 0) {
                [self refreshSizeCompletion:completion];
            }else {
                !completion ? : completion();
            }
        });
    }else {
        !completion ? : completion();
    }
}


- (void)registerClass:(nonnull Class)cellClass forViewReuseIdentifier:(NSString *)identifier
{
    [self.viewClsDict setObject:NSStringFromClass(cellClass) forKey:identifier];
}

- (__kindof LBMiddleExpandLoopViewBaseCell *)dequeueReusableCellWithIdentifier:(NSString *)identifier
{
    
    LBMiddleExpandLoopViewBaseCell *cell;
    for (LBMiddleExpandLoopViewBaseCell *cellReusable in self.reusableCells)
    {
        if ([cellReusable.reuseIdentifier isEqualToString:identifier]) {
            cell = cellReusable;
        }
    }
    if (!cell) {
        Class cellCls = NSClassFromString(self.viewClsDict[identifier]);
        cell = [[cellCls alloc] initWithReuseIdentifier:identifier];
        cell.userInteractionEnabled = NO;
    } else {
        [self.reusableCells removeObject:cell];
    }
    return cell;
}


- (void)scrollToCellAtIndex:(NSInteger)index animated:(BOOL)animated {
    if (index < self.realCount) {
        [self stopTimer];
        
        if (self.isInfiniteLoop) {
            self.timerIndex = self.realCount + index;
            [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(startTimer) object:nil];
            [self performSelector:@selector(startTimer) withObject:nil afterDelay:0.5];
        } else {
            self.timerIndex = index;
        }
        
        [self.scrollView setContentOffset:CGPointMake(self.cellSize.width * self.timerIndex, 0) animated:animated];
           
        [self setupCellsWithContentOffset:self.scrollView.contentOffset];
        [self updateVisibleCellAppearance];
    }
}

- (void)adjustCurrentCell {
    if (self.isAutoScroll && self.realCount > 0) {
        self.scrollView.contentOffset = CGPointMake(self.cellSize.width * self.timerIndex, 0);
    }
}

- (void)startTimer {
    if (self.realCount > 1 && self.isAutoScroll) {
        [self stopTimer];
        
//        NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:self.autoScrollTime target:self selector:@selector(timerUpdate) userInfo:nil repeats:YES];
//        [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
      //  self.timer = timer;
    }
}

- (void)stopTimer {
    if (self.timer) {
        [self.timer invalidate];
        self.timer = nil;
    }
}

#pragma mark Private Methods
- (void)initialization {
    // 初始化默认数据
    self.clipsToBounds      = YES;
    self.isAutoScroll       = YES;
    self.isInfiniteLoop     = YES;
    self.minimumCellAlpha   = 1.0f;
    self.autoScrollTime     = 3.0f;
    
    // 添加scrollView
    [self addSubview:self.scrollView];
}

- (void)initialScrollViewAndCellSize {
    self.originSize = self.bounds.size;
    [self updateScrollViewAndCellSize];
    
    // 默认选中
    if (self.defaultSelectIndex >= 0 && self.defaultSelectIndex < self.realCount) {
        [self handleCellScrollWithIndex:self.defaultSelectIndex];
    }
}

- (void)updateScrollViewAndCellSize {
    if (self.bounds.size.width <= 0 || self.bounds.size.height <= 0) return;
    
    // 设置cell尺寸
    self.cellSize = CGSizeMake(self.bounds.size.width - 2 * self.leftRightMargin, self.bounds.size.height);
    if (self.delegate && [self.delegate respondsToSelector:@selector(sizeForCellInCycleScrollView:)]) {
        self.cellSize = [self.delegate sizeForCellInCycleScrollView:self];
    }
    
    // 设置scrollView大小
    self.scrollView.frame = CGRectMake(0, 0, self.cellSize.width, self.cellSize.height);
    self.scrollView.contentSize = CGSizeMake(self.cellSize.width * self.showCount,0);
    self.scrollView.center = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));
    
    if (self.realCount > 1) {
        CGPoint offset = CGPointZero;
        
        if (self.isInfiniteLoop) { // 开启无限轮播
            // 滚动到第二组
            offset = CGPointMake(self.cellSize.width * (self.realCount + self.defaultSelectIndex), 0);
            self.timerIndex = self.realCount + self.defaultSelectIndex;
        }else {
            offset = CGPointMake(self.cellSize.width * self.defaultSelectIndex, 0);
            self.timerIndex = self.defaultSelectIndex;
        }
        
        [self.scrollView setContentOffset:offset animated:NO];
        
        // 自动轮播
        if (self.isAutoScroll) {
            [self startTimer];
        }
    }
    
    // 根据当前scrollView的offset设置显示的cell
    [self setupCellsWithContentOffset:self.scrollView.contentOffset];
    
    // 更新可见cell的显示
    [self updateVisibleCellAppearance];
}

- (void)setupCellsWithContentOffset:(CGPoint)offset {
    if (self.showCount == 0) return;
    if (self.cellSize.width <= 0 || self.cellSize.height <= 0) return;
    //计算_visibleRange
    CGFloat originX = self.scrollView.frame.origin.x == 0 ? 0.01 : self.scrollView.frame.origin.x;
    CGFloat originY = self.scrollView.frame.origin.y == 0 ? 0.01 : self.scrollView.frame.origin.y;
    
    CGPoint startPoint = CGPointMake(offset.x - originX, offset.y - originY);
    
    CGPoint endPoint = CGPointMake(offset.x + self.scrollView.frame.size.width + originX, offset.y + self.scrollView.frame.size.height + originY);
    
    NSInteger startIndex = 0;
    for (NSInteger i = 0; i < self.visibleCells.count; i++) {
        if (self.cellSize.width * (i + 1) > startPoint.x) {
            startIndex = i;
            break;
        }
    }
    
    NSInteger endIndex = startIndex;
    for (NSInteger i = self.visibleCells.count - 1; i >= startIndex; i --) {
        if (self.cellSize.width * i < endPoint.x) {
            endIndex = i;
            break;
        }
    }
    
    // 可见页分别向前向后扩展一个,提高效率
    startIndex = MAX(startIndex, 0);
    endIndex = MIN(endIndex, self.visibleCells.count - 1);
    self.visibleRange = NSMakeRange(startIndex, endIndex - startIndex + 1);
    
    for (NSInteger i = startIndex; i <= endIndex; i++) {
        [self addCellAtIndex:i];
    }
    
    for (NSInteger i = 0; i < startIndex; i ++) {
        [self removeCellAtIndex:i];
    }
    
    for (NSInteger i = endIndex + 1; i < self.visibleCells.count; i ++) {
        [self removeCellAtIndex:i];
    }
}

- (void)updateVisibleCellAppearance {
    if (self.showCount == 0) return;
    if (self.cellSize.width <= 0 || self.cellSize.height <= 0) return;
    
    CGFloat offsetX = self.scrollView.contentOffset.x;
    
    for (NSInteger i = self.visibleRange.location; i < NSMaxRange(self.visibleRange); i++) {
        LBMiddleExpandLoopViewBaseCell *cell = self.visibleCells[i];
        CGFloat originX = cell.frame.origin.x;
        CGFloat delta = fabs(originX - offsetX);
        CGRect originCellFrame = (CGRect){{self.cellSize.width * i, 0}, self.cellSize};
        
        CGFloat leftRightInset = 0;
        CGFloat topBottomInset = 0;
        CGFloat alpha = 0;
        
        if (delta < self.cellSize.width) {
            alpha = (delta / self.cellSize.width) * self.minimumCellAlpha;
            CGFloat adjustLeftRightMargin = self.leftRightMargin == 0 ? 0 : self.leftRightMargin + 1;
            CGFloat adjustTopBottomMargin = self.topBottomMargin == 0 ? 0 : self.topBottomMargin;
            
            leftRightInset = adjustLeftRightMargin * delta / self.cellSize.width;
            topBottomInset = adjustTopBottomMargin * delta / self.cellSize.width;
            
            NSInteger index = self.realCount == 0 ? 0 : i % self.realCount;
            if (index == self.currentSelectIndex) {
                [self.scrollView bringSubviewToFront:cell];
            }
        } else {
            alpha = self.minimumCellAlpha;
            
            leftRightInset = self.leftRightMargin;
            topBottomInset = self.topBottomMargin;
            
            [self.scrollView sendSubviewToBack:cell];
        }
        
        if (self.leftRightMargin == 0 && self.topBottomMargin == 0) {
            cell.frame = originCellFrame;
        }else {
            CGFloat scaleX = (self.cellSize.width - leftRightInset * 2) / self.cellSize.width;
            CGFloat scaleY = (self.cellSize.height - topBottomInset * 2) / self.cellSize.height;
            UIEdgeInsets insets = UIEdgeInsetsMake(topBottomInset, leftRightInset - 0.1, topBottomInset, leftRightInset);
            
            cell.layer.transform = CATransform3DMakeScale(scaleX, scaleY, 1.0);
            cell.frame = UIEdgeInsetsInsetRect(originCellFrame, insets);
        }
        
        // 获取当前cell
        if (cell.tag == self.currentSelectIndex) {
            self.currentCell = cell;
        }
    }
}

- (void)addCellAtIndex:(NSInteger)index {
    NSParameterAssert(index >= 0 && index < self.visibleCells.count);
    
    LBMiddleExpandLoopViewBaseCell *cell = self.visibleCells[index];
    if ((NSObject *)cell == [NSNull null]) {
        cell = [self.dataSource cycleScrollView:self cellForViewAtIndex:index % self.realCount];
        if (cell) {
            [self.visibleCells replaceObjectAtIndex:index withObject:cell];
            
            cell.tag = index % self.realCount;
            
            __weak __typeof(self) weakSelf = self;
            cell.didCellClick = ^(NSInteger index) {
                [weakSelf handleCellSelectWithIndex:index];
            };
            cell.frame = CGRectMake(self.cellSize.width * index, 0, self.cellSize.width, self.cellSize.height);
            if (!cell.superview) {
                [self.scrollView addSubview:cell];
            }
        }
    }
}

- (void)removeCellAtIndex:(NSInteger)index{
    LBMiddleExpandLoopViewBaseCell *cell = [self.visibleCells objectAtIndex:index];
    if ((NSObject *)cell == [NSNull null]) return;
    
    [self.reusableCells addObject:cell];
    
    if (cell.superview) {
        [cell removeFromSuperview];
    }
    
    [self.visibleCells replaceObjectAtIndex:index withObject:[NSNull null]];
}

- (void)handleCellSelectWithIndex:(NSInteger)index {
    if ([self.delegate respondsToSelector:@selector(cycleScrollView:didSelectCellAtIndex:)]) {
        [self.delegate cycleScrollView:self didSelectCellAtIndex:index];
    }
}

- (void)handleCellScrollWithIndex:(NSInteger)index {
    self.currentSelectIndex = index;
    // 获取当前cell
    for (NSInteger i = self.visibleRange.location; i < NSMaxRange(self.visibleRange); i++) {
        LBMiddleExpandLoopViewBaseCell *cell = self.visibleCells[i];
        if (cell.tag == index) {
            self.currentCell = cell;
        }
    }
    
    if ([self.delegate respondsToSelector:@selector(cycleScrollView:didScrollCellToIndex:)]) {
        [self.delegate cycleScrollView:self didScrollCellToIndex:index];
    }
}

- (void)timerUpdate {
    self.timerIndex++;
    
    // bug fixed:解决反向滑动停止后,可能出现的自动滚动错乱问题
    if (self.timerIndex > self.realCount * 2) {
        self.timerIndex = self.realCount * 2;
    }
    
    if (!self.isInfiniteLoop) {
        if (self.timerIndex >= self.realCount) {
            self.timerIndex = 0;
        }
    }
    
    [self.scrollView setContentOffset:CGPointMake(self.cellSize.width * self.timerIndex, 0) animated:YES];
}

#pragma mark UIScrollView Delegate
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
    if (self.realCount == 0) return;
    if (self.cellSize.width <= 0 || self.cellSize.height <= 0) return;
    
    NSInteger index = 0;
    index = (NSInteger)round(self.scrollView.contentOffset.x / self.cellSize.width) % self.realCount;
    
    if (self.isInfiniteLoop) {
        if (self.realCount > 1) {
            CGFloat horIndex = scrollView.contentOffset.x / self.cellSize.width;
            if (horIndex >= 2 * self.realCount) {
                scrollView.contentOffset = CGPointMake(self.cellSize.width * self.realCount, 0);
                self.timerIndex = self.realCount;
            }
            
            if (horIndex <= (self.realCount - 1)) {
                scrollView.contentOffset = CGPointMake(self.cellSize.width * (2 * self.realCount - 1), 0);
                self.timerIndex = 2 * self.realCount - 1;
            }
        }
        }else {
            index = 0;
        }
    
    [self setupCellsWithContentOffset:scrollView.contentOffset];
    [self updateVisibleCellAppearance];
    
    if (index >= 0 && self.currentSelectIndex != index) {
        [self handleCellScrollWithIndex:index];
    }
    [self handleScrollViewDidScroll:scrollView];
}

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
    [self stopTimer];
    if ([self.delegate respondsToSelector:@selector(cycleScrollView:willBeginDragging:)]) {
        [self.delegate cycleScrollView:self willBeginDragging:scrollView];
    }
}

// 结束拖拽时调用,decelerate是否有减速
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
    if (!decelerate) {
        [self startTimer];
    }
    if ([self.delegate respondsToSelector:@selector(cycleScrollView:didEndDragging:willDecelerate:)]) {
        [self.delegate cycleScrollView:self didEndDragging:scrollView willDecelerate:decelerate];
    }
}

// 结束减速是调用
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    [self startTimer];
    if ([self.delegate respondsToSelector:@selector(cycleScrollView:didEndDecelerating:)]) {
        [self.delegate cycleScrollView:self didEndDecelerating:scrollView];
    }
}

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
    if (self.realCount > 1 && self.isAutoScroll) {
        NSInteger index = round(targetContentOffset->x / self.cellSize.width);
        self.timerIndex = index;
    }
}

- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView {
    [self updateVisibleCellAppearance];
    
    if ([self.delegate respondsToSelector:@selector(cycleScrollView:didEndScrollingAnimation:)]) {
        [self.delegate cycleScrollView:self didEndScrollingAnimation:scrollView];
    }
}

- (void)handleScrollViewDidScroll:(UIScrollView *)scrollView {
    if ([self.delegate respondsToSelector:@selector(cycleScrollView:didScroll:)]) {
        [self.delegate cycleScrollView:self didScroll:scrollView];
    }
    
    if ([self.delegate respondsToSelector:@selector(cycleScrollView:scrollingFromIndex:toIndex:ratio:)]) {
        BOOL isFirstRevirse = NO; // 是否在第一个位置反向滑动
        CGFloat ratio = 0;   // 滑动百分比
        CGFloat offsetX = scrollView.contentOffset.x;
        CGFloat maxW = self.realCount * scrollView.bounds.size.width;
        
        CGFloat changeOffsetX = self.isInfiniteLoop ? (offsetX - maxW) : offsetX;
        if (changeOffsetX < 0) {
            changeOffsetX = -changeOffsetX;
            isFirstRevirse = YES;
        }
        ratio = (changeOffsetX / scrollView.bounds.size.width);
        if (ratio > self.realCount || ratio < 0) return; // 越界,不作处理
        ratio = MAX(0, MIN(self.realCount, ratio));
        NSInteger baseIndex = floor(ratio);
        if (baseIndex + 1 > self.realCount) {
            // 右边越界了
            baseIndex = 0;
        }
        CGFloat remainderRatio = ratio - baseIndex;
        if (remainderRatio <= 0 || remainderRatio >= 1) return;
        NSInteger toIndex = 0;
        if (isFirstRevirse) {
            baseIndex = self.realCount - 1;
            toIndex = 0;
            remainderRatio = 1 - remainderRatio;
        }else if (baseIndex == self.realCount - 1) {
            toIndex = 0;
        }else {
            toIndex = baseIndex + 1;
        }
        [self.delegate cycleScrollView:self scrollingFromIndex:baseIndex toIndex:toIndex ratio:remainderRatio];
    }
}

#pragma mark - 懒加载
- (UIScrollView *)scrollView {
    if (!_scrollView) {
        _scrollView = [[UIScrollView alloc] initWithFrame:self.bounds];
        _scrollView.scrollsToTop = NO;
        _scrollView.delegate = self;
        _scrollView.pagingEnabled = YES;
        _scrollView.clipsToBounds = NO;
        _scrollView.showsHorizontalScrollIndicator = NO;
        _scrollView.showsVerticalScrollIndicator = NO;
    }
    return _scrollView;
}

- (NSMutableArray *)visibleCells {
    if (!_visibleCells) {
        _visibleCells = [NSMutableArray new];
    }
    return _visibleCells;
}

- (NSMutableDictionary *)viewClsDict
{
    if (!_viewClsDict) {
        _viewClsDict = [NSMutableDictionary dictionary];
    }
    return _viewClsDict;
}

- (NSMutableArray *)reusableCells {
    if (!_reusableCells) {
        _reusableCells = [NSMutableArray new];
    }
    return _reusableCells;
}

@end

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1034825.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

计算机基础 堆和栈

首先我们需要知道的是栈和堆是两种数据结构 1.栈和堆的定义 栈&#xff1a;是一种先进后出的数据结构&#xff0c;是一种线性结构 堆是一种树形结构&#xff0c;是一颗完全二叉树&#xff0c; 其存储的元素可以通过指针或引用访问 最大堆【大根堆】 &#xff1a;堆中的每一个…

js-cookie使用 js深度克隆(判断引用类型是数组还是对象的方法)

cookie和深度拷贝的使用 1、js-cookie使用2、js深度克隆 1、js-cookie使用 前端的本地存储分为 localstorage、sesstionstorage、cookie 但是咱们有时候需要做7天免登录的需求时&#xff0c;选择 cookie 作为前端的本地存储是在合适不过的了 直接操作 cookie 可以&#xff0c; …

ModbusTCP 转 Profinet 主站网关在博图配置案例

兴达易控ModbusTCP转Profinet网关&#xff0c;在 Profinet 侧做为 Profinet 主站控制器&#xff0c;接 Profinet 设备&#xff0c;如伺服驱动器&#xff1b;兴达易控ModbusTCP 和 Profinet网关在 ModbusTCP 侧做为 ModbusTCP 从站&#xff0c;接 PLC、上位机、wincc 屏等。 拓扑…

Spring事务1+入门案例(简约银行转账)

0、事务基础概念 1.事务角色&#xff1a; 2.事务相关配置 一、配置文件的书写 1.JDBC配置文件 public class JdbcConfig {Value("${jdbc.driver}")private String driver;Value("${jdbc.url}")private String url;Value("${jdbc.username}")p…

win11安装h3c lab无法启动putty终端的解决方法

文章目录 问题记录作者自己的解决方法第一步&#xff1a;安装MobaXterm第二步&#xff1a;修改h3c lab的终端工具 问题记录 win11可以同时安装vm、virtualBox、typer-v安装&#xff0c;因为在最新的系统中已经兼容但是可能出现win11安装h3c lab无法启动putty终端的问题&#x…

数据结构_顺序表_尾插、尾删、头插、头删(附带详解)

文章目录 前言一. 线性表二. 顺序表 - - - 数组2.1 什么是顺序表2.2 顺序表一般可以分为2.2.1 静态顺序表&#xff08;使用定长数组存储元素&#xff09;2.2.2 动态顺序表&#xff1a;使用动态开辟的数组存储2.2.3 顺序表的接口实现 三. SeqList.c 中各个接口的实现。3.1 初始化…

Java实现Modbus Tcp协议读写模拟工具数据

标题 前言一、读写模拟工具中数据(1) 定义Controller层(2) 定义Service层实现 二、调试(1) 读数据(2) 向寄存器写单个数据(3) 向寄存器写多个数据 前言 参考文章&#xff1a;https://www.cnblogs.com/ioufev/p/10831289.html 该文中谈及常见的几种读取设备数据实现&#xff0…

无硬盘的版本 1099,14寸笔记本,而且无硬盘的,特别有有意思,可以自己购买个硬盘,安装linux系统或者windows。

1&#xff0c;千元笔记本&#xff0c;金属外壳 有人进行评测了&#xff1a; https://www.bilibili.com/video/BV1Td4y1K7Cp 1499元的全新笔记本&#xff0c;有什么猫腻&#xff1f; 看了下价格&#xff0c;现在还优惠400&#xff0c;变成了1099。 https://item.jd.com/100851…

Django — 请求和响应

目录 一、请求1、概念2、请求参数方式分类3、案例3.1、URL 路径参数3.2、查询字符串参数3.3、form 表单传参3.4、Json 格式参数3.5、上传文件 二、响应1、HttpResponse2、JsonResponse 三、GET 和 POST 区别1、相同点2、不同点 一、请求 1、概念 请求&#xff08;Request&…

DDR4 眼图测试方法

DDR的全拼是Double Data Rate SDRAM双倍数据速率同步动态随机存取内存。主要就是用在电脑的内存。他的特点就是走线数量多&#xff0c;速度快&#xff0c;操作复杂&#xff0c;给测试和分析带来了很大的挑战。目前DDR技术已经发展到了DDR5&#xff0c;性能更高&#xff0c;功耗…

【算法练习Day4】 两两交换链表节点删除链表倒数第 N 个结点环形链表 II

​ ​&#x1f4dd;个人主页&#xff1a;Sherry的成长之路 &#x1f3e0;学习社区&#xff1a;Sherry的成长之路&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;练题 &#x1f3af;长路漫漫浩浩&#xff0c;万事皆有期待 文章目录 两两交换链表中的节点一…

毫米波雷达 TI IWR1443 在 ROS 中进行 octomap 建图

个人实验记录 /mmwave_ti_ros/ros_driver/src/ti_mmwave_rospkg/launch/1443_multi_3d_0.launch <launch><!-- Input arguments --><arg name"device" value"1443" doc"TI mmWave sensor device type [1443, 1642]"/><arg…

一例“msvc编译器O2优化导致的崩溃”的分析

1. 初步分析 某进程崩溃必现。 打开崩溃dmp&#xff0c;结合c源代码&#xff0c;崩溃大致发生在某dll代码里的这句&#xff1a;SAFE_DELETE(pContentData); En_HP_HandleResult CTcpOperation::OnClintReceive(HP_Client pSender, HP_CONNID dwConnID, const BYTE * pdata, i…

组队竞赛(int溢出问题)

目录 一、题目 二、代码 &#xff08;一&#xff09;没有注意int溢出 &#xff08;二&#xff09;正确代码 1. long long sum0 2. #define int long long 3. 使用现成的sort函数 一、题目 二、代码 &#xff08;一&#xff09;没有注意int溢出 #include <iostream&g…

机器学习的主要内容

分类任务 回归任务 有一些算法只能解决回归问题有一些算法只能解决分类问题有一些算法的思路既能解决回归问题&#xff0c;又能解决分类问题 一些情况下&#xff0c; 回归任务可以转化为分类任务&#xff0c; 比如我们预测学生的成绩&#xff0c;然后根据学生的成绩划分为A类、…

js制作柱状图的x轴时间, 分别展示 月/周/日 的数据

背景 有个需求是要做一个柱状图, x 轴是时间, y 轴是数量. 其中 x 轴的时间有三种查看方式: 月份/周/日, 也就是分别查看从当前日期开始倒推的最近每月/每周/每日的数量. 本篇文章主要是用来制作三种不同的 x 轴 从当前月开始倒推月份 注意 getMonth() 函数可以获取当前月份…

【【萌新的FPGA学习之实战流水灯】】

萌新的FPGA学习之实战流水灯 实验任务 本节的实验任务是使用领航者底板上的两个 PL LED 灯顺序点亮并熄灭&#xff0c;循环往复产生流水灯的效 果&#xff0c;流水间隔时间为 0.5s。 1MHz&#xff1d;1000000Hz 10的6次方 1ns&#xff1d;10的-9次方秒 开发板晶振50Mhz 计算得…

如何看待Unity新的收费模式?

文章目录 背景Unity的论点开发者的担忧如何看待Unity新的收费模式&#xff1f;1. 理解Unity的立场2. 考虑小型开发者3. 探索替代方案4. 对市场变化保持敏感5. 提高游戏质量 结论 &#x1f389; 如何看待Unity新的收费模式&#xff1f; ☆* o(≧▽≦)o *☆嗨~我是IT陈寒&#x1…

R语言柱状图直方图 histogram

柱状图简介 柱状图也叫直方图&#xff0c;是展示连续性数值的分布状况。在x轴上将连续型数值分为一定数量的组&#xff0c;y轴显示对应值的频数。 R基本的柱状图 hist 我们用R自带的Orange数据来画图。 > head(Orange)Tree age circumference(圆周长) 1 1 118 …

Docker搭建DNS服务器--nouse

前言 DNS服务器是(Domain Name System或者Domain Name Service)域名系统或者域名服务,域名系统为Internet上的主机分配域名地址和IP地址。 安装 2.1 实验环境 IP 系统版本 角色 192.168.40.121 Ubuntu 22.10 DNS服务器 192.168.40.122 Ubuntu 22.10 测试机器 2.2 …