Мозаичное световое шоу CAReplicatorLayer анимация - PullRequest
0 голосов
/ 28 октября 2019

Я пытаюсь добиться этого эффекта светового шоу мозаики для моего фона с помощью объекта CAReplicatorLayer: https://downloops.com/stock-footage/mosaic-light-show-blue-illuminated-pixel-grid-looping-background/

enter image description here

Каждая плитка /CALayer - это одно изображение, которое было скопировано по горизонтали и вертикали. Та часть, которую я выполнил.

Мне кажется, эта задача разбита как минимум на 4 отдельные части:

  1. Выберите случайный тайл
  2. Выберите случайный диапазонсмещения цвета для выбранной плитки
  3. Примените это смещение цвета за указанную продолжительность в секундах
  4. Если случайное смещение цвета превышает определенный порог, тогда примените эффект свечения с анимацией смещения цвета.

Но я не совсем уверен, что это будет правильный алгоритм.

Мой текущий код был взят из этого урока: https://www.swiftbysundell.com/articles/ca-gems-using-replicator-layers-in-swift/

Анимации не моисильный набор, и я на самом деле не знаю, как применить непрерывную / повторяющуюся анимацию ко всем плиткам. Вот мой текущий код:

    @IBOutlet var animationView: UIView!

    func cleanUpAnimationView() {
        self.animationView.layer.removeAllAnimations()
        self.animationView.layer.sublayers?.removeAll()
    }

    /// Start a background animation with a replicated pattern image in tiled formation.
    func setupAnimationView(withPatternImage patternImage: UIImage, animate: Bool = true) {
        // Tutorial: https://www.swiftbysundell.com/articles/ca-gems-using-replicator-layers-in-swift/

        let imageSize = patternImage.size.halve

        self.cleanUpAnimationView()

        // Animate pattern image
        let replicatorLayer = CAReplicatorLayer()
        replicatorLayer.frame.size = self.animationView.frame.size
        replicatorLayer.masksToBounds = true
        self.animationView.layer.addSublayer(replicatorLayer)

        // Give the replicator layer a sublayer to replicate
        let imageLayer = CALayer()
        imageLayer.contents = patternImage.cgImage
        imageLayer.frame.size = imageSize
        replicatorLayer.addSublayer(imageLayer)

        // Tell the replicator layer how many copies (or instances) of the image needs to be rendered. But we won't see more than one since they are, per default, all rendered/stacked on top of each other.
        let instanceCount = self.animationView.frame.width / imageSize.width
        replicatorLayer.instanceCount = Int(ceil(instanceCount))
        // Instance offsets & transforms is needed to move them
        // 'CATransform3D' transform will be used on each instance: shifts them to the right & reduces the red & green color component of each instance's tint color.

        // Shift each instance by the width of the image
        replicatorLayer.instanceTransform = CATransform3DMakeTranslation(imageSize.width, 0, 0)

        // Reduce the red & green color component of each instance, effectively making each copy more & more blue while horizontally repeating the gradient pattern
        let colorOffset = -1 / Float(replicatorLayer.instanceCount)
        replicatorLayer.instanceRedOffset = colorOffset
        replicatorLayer.instanceGreenOffset = colorOffset
        //replicatorLayer.instanceBlueOffset = colorOffset
        //replicatorLayer.instanceColor = UIColor.random.cgColor

        // Extend the original pattern to also repeat vertically using another tint color gradient
        let verticalReplicatorLayer = CAReplicatorLayer()
        verticalReplicatorLayer.frame.size = self.animationView.frame.size
        verticalReplicatorLayer.masksToBounds = true
        verticalReplicatorLayer.instanceBlueOffset = colorOffset
        self.animationView.layer.addSublayer(verticalReplicatorLayer)

        let verticalInstanceCount = self.animationView.frame.height / imageSize.height
        verticalReplicatorLayer.instanceCount = Int(ceil(verticalInstanceCount))

        verticalReplicatorLayer.instanceTransform = CATransform3DMakeTranslation(0, imageSize.height, 0)
        verticalReplicatorLayer.addSublayer(replicatorLayer)

        guard animate else { return }

        // Set both the horizontal & vertical replicators to add a slight delay to all animations applied to the layer they're replicating
        let delay = TimeInterval(0.1)
        replicatorLayer.instanceDelay = delay
        verticalReplicatorLayer.instanceDelay = delay

        // This will make the image layer change color
        let animColor = CABasicAnimation(keyPath: "instanceRedOffset")
        animColor.duration = animationDuration
        animColor.fromValue = verticalReplicatorLayer.instanceRedOffset
        animColor.toValue = -1 / Float(Int.random(replicatorLayer.instanceCount-1))
        animColor.autoreverses = true
        animColor.repeatCount = .infinity
        replicatorLayer.add(animColor, forKey: "colorshift")

        let animColor1 = CABasicAnimation(keyPath: "instanceGreenOffset")
        animColor1.duration = animationDuration
        animColor1.fromValue = verticalReplicatorLayer.instanceGreenOffset
        animColor1.toValue = -1 / Float(Int.random(replicatorLayer.instanceCount-1))
        animColor1.autoreverses = true
        animColor1.repeatCount = .infinity
        replicatorLayer.add(animColor1, forKey: "colorshift1")

        let animColor2 = CABasicAnimation(keyPath: "instanceBlueOffset")
        animColor2.duration = animationDuration
        animColor2.fromValue = verticalReplicatorLayer.instanceBlueOffset
        animColor2.toValue = -1 / Float(Int.random(replicatorLayer.instanceCount-1))
        animColor2.autoreverses = true
        animColor2.repeatCount = .infinity
        replicatorLayer.add(animColor2, forKey: "colorshift2")
    }
...