LNCollectionView-碰撞效果

122 阅读5分钟

引言

这篇文章同样来做一些有趣的事情,我们会在两个LNScrollView之间构建一些联系;我们使用冲量来实现这些联系,一些LNScrollView可以检测出达到边界后剩余的,并把这些动量传递给另外的LNScrollView;类似于打台球,所以它也会有两种传递模式,看起来分别是:

1.球杆撞击白球

2.白球撞击彩色球

Github-LNCollectionView

一些原理

场景1的情况,简化处理为:直接把A的动量都给到B上,A的动量变成0。

m2v2=m1v1+m2v2m_2v_2'= m_1v_1 + m_2v_2
v1=0v_1' = 0

场景2的情况下,同时满足动量守恒、能量守恒,用以下两个方程保证这两个守恒:

m1v1+m2v2=m1v1+m2v2m_1v_1 + m_2v_2 = m_1v_1' + m_2v_2'
12m1v12+12m2v22=12m1v12+12m2v22\frac{1}{2}m_1v_1^2 + \frac{1}{2}m_2v_2^2 = \frac{1}{2}m_1v_1'^2 + \frac{1}{2}m_2v_2'^2

分层和正方向

我们对这种连接结构划分了三层:

1.generator:产生器,这个零件会在当前控件中检测到多余的动量,并把他们发射出去。

2.convertor:转换头,这个零件会把一次碰撞结果传递给Pulser或是反馈给generator。

3.pulser:发生器,这个零件会接收一个动量并且在自己的控件上产生效果。

我们需要规定一下正方向,下面这张图片展示了这里对正方向的指定:

pAoHmHf.png

在图中,绿色箭头的指向为产生器的正确箭头,他们以向外为正方向;红色箭头的指向为发生器,他们以向内为正方向。OK,也就是说,一个generator产生的向外的动量,转化到Pulser中时,它是向内的,论上下左右;例如:一个顶部generator检测到一个向上的冲击:在它被连接在一个左侧的Pulser时,Pulser会产生一个向右的动量;当他被连接在一个右侧的Pulser时,Pulser会产生一个向左的动量。

事实上,每个产生器只能检测到自己正方向的运动,而每个发生器可以感知到来自产生器的状态和自身控件当前的状态,自身的状态可能是正负两个方向。

代码

Generator:

@interface LNScrollViewPulseGenerator : NSObject
@property (nonatomic, assign) CGFloat mass;
@property (nonatomic, assign, readonly) BOOL isOpen;
@property (nonatomic, weak) NSObject<LNScrollViewPulseGeneratorDelegate> *delegate;
- (CGFloat)generate:(CGFloat)velocity;
- (void)open;
- (void)close;
@end

Generator的固有属性:

  • mass:质量,这个属性决定碰撞时的强度;质量更大的对象碰撞时产生更强的冲击,也越不容易产生位移。
  • isOpen:是否开启这个检查器;默认是关闭的,开启后会检测出剩余冲量并发射出去。
  • generate:产生一个速度为V的动量,generator.mass的动量。
  • delegate: 这个delegate通常是Convertor,把自己产生的动量传递过去。

Pulser:

@interface LNScrollViewPulser : NSObject
@property (nonatomic, weak) NSObject<LNScrollViewPulserDelegate> *delegate;
@property (nonatomic, assign) CGFloat mass;
@property (nonatomic, assign, readonly) BOOL isOpen;
- (LNScrollViewMomentum *)getCurrentMomentum;
- (void)updateMomentum:(LNScrollViewMomentum *)momentum;
- (void)open;
- (void)close;
@end

Pulser:

  • mass:同Generator的质量,他决定Pulser受冲击的影响,质量大影响越小。
  • iOpen:同Generator,开启时会把接收到的动量作用到自己的控件上。
  • getCurrentMomentum:从接收方获取当前已存在的动量;这和台球的不太一样,台球一般只会撞击静止的彩色球,但是这里可以打移动的靶子。
  • updateMomentum:一次撞击结束后,目标控件接收到的动量,Pulser把这个动量转化成动画效果。
  • open/close:开启、关闭。

Convertor:

@interface LNScrollViewPulseConvertor : NSObject <LNScrollViewPulseGeneratorDelegate>
@property (nonatomic, assign) BOOL isConversationOfEnergy;
- (void)bindGenerator:(LNScrollViewPulseGenerator *)generator;
- (void)bindPulser:(LNScrollViewPulser *)pulser;
@end

isConversationOfEnergy:是否考虑能量转化;开启时,类似白球撞彩色球;关闭,类似球杆撞白色。 bindGenerator:绑定一个产生器(设置一下代理)。 bindPulser:绑定一个发生器(设置一下代理)。 转化代码:

- (LNScrollViewMomentum *)generatorHasDetectedMomentum:(LNScrollViewMomentum *)momentum
{
    if (self.isConversationOfEnergy) {
        LNScrollViewMomentum *pulserMomentum = [self.pulser getCurrentMomentum];
        LNScrollViewMomentum *targetMomentum = [[LNScrollViewMomentum alloc] init];
        targetMomentum.mass = pulserMomentum.mass;
        targetMomentum.velocity = (2 * momentum.mass * momentum.velocity + pulserMomentum.mass * pulserMomentum.velocity - momentum.mass * pulserMomentum.velocity)/(momentum.mass + pulserMomentum.mass);
        [self.pulser updateMomentum:targetMomentum];
        LNScrollViewMomentum *feedbackMomentum = [[LNScrollViewMomentum alloc] init];
        feedbackMomentum.mass = momentum.mass;
        feedbackMomentum.velocity = (2 * pulserMomentum.mass * pulserMomentum.velocity + momentum.mass * momentum.velocity - pulserMomentum.mass * momentum.velocity)/(momentum.mass + pulserMomentum.mass);
        return feedbackMomentum;
    } else {
        LNScrollViewMomentum *pulserMomentum = [**self**.pulser getCurrentMomentum];
        LNScrollViewMomentum *targetMomentum = [[LNScrollViewMomentum alloc] init];
        targetMomentum.mass = pulserMomentum.mass;
        targetMomentum.velocity = (momentum.mass * momentum.velocity + pulserMomentum.mass * pulserMomentum.velocity)/pulserMomentum.mass;
        [self.pulser updateMomentum:targetMomentum];
        LNScrollViewMomentum *feedbackMomentum = [[LNScrollViewMomentum alloc] init];
        feedbackMomentum.mass = momentum.mass;
        feedbackMomentum.velocity = 0.f;
        return feedbackMomentum;
    }
}

有isConversationOfEnergy的时候,会产生一个反馈的动量;关闭的时候,反馈的冲量总是0,所以当你连接ScrollViewA和ScrollViewB时,当这个转接头开启了有isConversationOfEnergy的时候,A会接收到一个反向的动量;否则A会立即停止。

LNScrollView:

@interface LNScrollView : UIView
@property (nonatomic, strong, readonly) LNScrollViewPulseGenerator *topPulseGenerator;
@property (nonatomic, strong, readonly) LNScrollViewPulseGenerator *leftPulseGenerator;
@property (nonatomic, strong, readonly) LNScrollViewPulseGenerator *bottomPulseGenerator;
@property (nonatomic, strong, readonly) LNScrollViewPulseGenerator *rightPulseGenerator;
@property (nonatomic, strong, readonly) LNScrollViewPulser *topPulser;
@property (nonatomic, strong, readonly) LNScrollViewPulser *leftPulser;
@property (nonatomic, strong, readonly) LNScrollViewPulser *bottomPulser;
@property (nonatomic, strong, readonly) LNScrollViewPulser *rightPulser;
@end

每个ScrollView有4个Generator和4个Pulser;对应四个方向,每个方向的正方向此前我们已经定义好了。可以将任意方向的Generator和Pulser连接到一起去,像这样:

- (void)viewDidLoad {
    [super viewDidLoad];
    [self.view addSubview:self.collectionView1];
    [self.view addSubview:self.collectionView2];
    [self.rightToLeftConvertor bindGenerator:self.collectionView1.rightPulseGenerator];
    [self.rightToLeftConvertor bindPulser:self.collectionView2.leftPulser];
    self.rightToLeftConvertor.isConversationOfEnergy = YES;
    self.collectionView1.rightPulseGenerator.mass = 1.f;
    self.collectionView2.leftPulser.mass = 1.f;
    [self.leftToRightConvertor bindGenerator:self.collectionView2.leftPulseGenerator];
    [self.leftToRightConvertor bindPulser:self.collectionView1.rightPulser];
    self.leftToRightConvertor.isConversationOfEnergy = YES;
    self.collectionView1.rightPulser.mass = 1.f;
    self.collectionView2.leftPulseGenerator.mass = 1.f;
}

上边的代码collectionView1的右Generator和collectionView2的左Generator绑定到了一个转接头上,这样collectionView1在滚到右侧时会产生一个动量,传递给collectionView2的左侧发生器,collectionView2会继承一个转化后的初始速度。

同样的,将collectionView2的左Generator和collectionView1的右Generator绑定到一个转接头上,这样collectionView2在滚到左侧的时候会产生一个动量,传递给collectionView1的右侧发生器,collectionView1会继承一个转化后的初速度。

(这个有什么用,它可以用在列表嵌套时做内外层互动)

总结

本文讲述了多个LNCollectionView之间互动的实现方法,主要思路是模拟LNcollectionView之间发生碰撞,从而对速度进行重新分配;使用Generator->convertor->pulser三层结构实现的不同方向任意组合,最终实现了不同控件之间的联动效果。 (这些算式不保证符合现实hhhh,大致做成这样)