[WIP] Stickers editor

This commit is contained in:
Ilya Laktyushin
2024-04-07 18:39:05 +04:00
parent cf9699a1ba
commit a51c65e268
9 changed files with 151 additions and 21 deletions

View File

@@ -18,6 +18,7 @@ final class StickerCutoutOutlineView: UIView {
let strokeLayer = SimpleShapeLayer()
let imageLayer = SimpleLayer()
var outlineLayer = CAEmitterLayer()
var outline2Layer = CAEmitterLayer()
var glowLayer = CAEmitterLayer()
override init(frame: CGRect) {
@@ -56,23 +57,32 @@ final class StickerCutoutOutlineView: UIView {
}
private func setupAnimation(path: BezierPath) {
self.outlineLayer.removeFromSuperlayer()
self.outline2Layer.removeFromSuperlayer()
self.glowLayer.removeFromSuperlayer()
self.outlineLayer = CAEmitterLayer()
self.outlineLayer.opacity = 0.7
self.outlineLayer.opacity = 0.77
self.outline2Layer = CAEmitterLayer()
self.outline2Layer.opacity = 0.7
self.glowLayer = CAEmitterLayer()
self.layer.addSublayer(self.outlineLayer)
// self.layer.addSublayer(self.outline2Layer)
self.layer.addSublayer(self.glowLayer)
let randomBeginTime = (previousBeginTime + 4) % 6
previousBeginTime = randomBeginTime
let duration = min(5.0, max(2.0, path.length / 2200.0))
let duration = min(6.0, max(2.5, path.length / 2200.0))
let outlineAnimation = CAKeyframeAnimation(keyPath: "emitterPosition")
outlineAnimation.path = path.path.cgPath
outlineAnimation.duration = duration
outlineAnimation.repeatCount = .infinity
outlineAnimation.calculationMode = .paced
outlineAnimation.fillMode = .forwards
outlineAnimation.beginTime = Double(randomBeginTime)
self.outlineLayer.add(outlineAnimation, forKey: "emitterPosition")
@@ -85,14 +95,49 @@ final class StickerCutoutOutlineView: UIView {
lineEmitterCell.color = UIColor.white.cgColor
lineEmitterCell.contents = UIImage(named: "Media Editor/ParticleDot")?.cgImage
lineEmitterCell.lifetime = 2.2
lineEmitterCell.birthRate = 120
lineEmitterCell.scale = 0.14
lineEmitterCell.birthRate = 700
lineEmitterCell.scale = 0.17
lineEmitterCell.alphaSpeed = -0.4
self.outlineLayer.emitterCells = [lineEmitterCell]
self.outlineLayer.emitterMode = .points
self.outlineLayer.emitterSize = CGSize(width: 1.0, height: 1.0)
self.outlineLayer.emitterShape = .point
self.outlineLayer.emitterSize = CGSize(width: 1.33, height: 1.33)
self.outlineLayer.emitterShape = .rectangle
let outline2Animation = CAKeyframeAnimation(keyPath: "emitterPosition")
outline2Animation.path = path.path.cgPath
outline2Animation.duration = duration
outline2Animation.repeatCount = .infinity
outline2Animation.calculationMode = .paced
outline2Animation.fillMode = .forwards
outline2Animation.beginTime = Double(randomBeginTime) + 0.02
self.outline2Layer.add(outline2Animation, forKey: "emitterPosition")
let line2EmitterCell = CAEmitterCell()
line2EmitterCell.beginTime = CACurrentMediaTime()
let line2AlphaBehavior = createEmitterBehavior(type: "valueOverLife")
line2AlphaBehavior.setValue("color.alpha", forKey: "keyPath")
line2AlphaBehavior.setValue([0.0, 0.5, 0.8, 0.5, 0.0], forKey: "values")
line2EmitterCell.setValue([line2AlphaBehavior], forKey: "emitterBehaviors")
line2EmitterCell.color = UIColor.white.cgColor
line2EmitterCell.contents = UIImage(named: "Media Editor/ParticleDot")?.cgImage
line2EmitterCell.lifetime = 2.2
line2EmitterCell.birthRate = 500
line2EmitterCell.scale = 0.14
line2EmitterCell.alphaSpeed = -0.4
self.outline2Layer.emitterCells = [line2EmitterCell]
self.outline2Layer.emitterMode = .points
self.outline2Layer.emitterSize = CGSize(width: 1.5, height: 1.5)
self.outline2Layer.emitterShape = .circle
let glowAnimation = CAKeyframeAnimation(keyPath: "emitterPosition")
glowAnimation.path = path.path.cgPath
@@ -123,6 +168,7 @@ final class StickerCutoutOutlineView: UIView {
self.strokeLayer.animateAlpha(from: 0.0, to: CGFloat(self.strokeLayer.opacity), duration: 0.4)
self.outlineLayer.animateAlpha(from: 0.0, to: CGFloat(self.outlineLayer.opacity), duration: 0.4, delay: 0.0)
self.outline2Layer.animateAlpha(from: 0.0, to: CGFloat(self.outline2Layer.opacity), duration: 0.4, delay: 0.0)
self.glowLayer.animateAlpha(from: 0.0, to: CGFloat(self.glowLayer.opacity), duration: 0.4, delay: 0.0)
let values = [1.0, 1.07, 1.0]
@@ -133,6 +179,7 @@ final class StickerCutoutOutlineView: UIView {
override func layoutSubviews() {
self.strokeLayer.frame = self.bounds.offsetBy(dx: 0.0, dy: 1.0)
self.outlineLayer.frame = self.bounds
self.outline2Layer.frame = self.bounds
self.imageLayer.frame = self.bounds
self.glowLayer.frame = self.bounds
}
@@ -152,8 +199,8 @@ private func getPathFromMaskImage(_ image: CIImage, size: CGSize, values: MediaE
return nil
}
contour = simplify(contour, tolerance: 1.4)
let path = BezierPath(points: contour, smooth: false)
contour = simplify(contour, tolerance: 1.0)
let path = BezierPath(points: contour, smooth: true)
let firstScale = min(size.width, size.height) / 256.0
let secondScale = size.width / 1080.0
@@ -494,8 +541,59 @@ private class BezierPath {
init(points: [CGPoint], smooth: Bool) {
self.path = UIBezierPath()
if smooth {
if points.count < 3 {
self.path.move(to: points.first ?? CGPoint.zero)
self.path.addLine(to: points[1])
self.length = points[1].distanceFrom(points[0])
return
} else {
self.path.move(to: points.first!)
let n = points.count - 1
let tension = 0.5
for i in 0 ..< n {
let currentPoint = points[i]
var nextIndex = (i + 1) % points.count
var prevIndex = i == 0 ? points.count - 1 : i - 1
var nextNextIndex = (nextIndex + 1) % points.count
let prevPoint = points[prevIndex]
let nextPoint = points[nextIndex]
let nextNextPoint = points[nextNextIndex]
let d1 = sqrt(pow(currentPoint.x - prevPoint.x, 2) + pow(currentPoint.y - prevPoint.y, 2))
let d2 = sqrt(pow(nextPoint.x - currentPoint.x, 2) + pow(nextPoint.y - currentPoint.y, 2))
let d3 = sqrt(pow(nextNextPoint.x - nextPoint.x, 2) + pow(nextNextPoint.y - nextPoint.y, 2))
var controlPoint1: CGPoint
if d1 < 0.0001 {
controlPoint1 = currentPoint
} else {
controlPoint1 = CGPoint(x: currentPoint.x + (tension * d2 / (d2 + d3)) * (nextPoint.x - prevPoint.x),
y: currentPoint.y + (tension * d2 / (d2 + d3)) * (nextPoint.y - prevPoint.y))
}
prevIndex = i
nextIndex = (i + 1) % points.count
nextNextIndex = (nextIndex + 1) % points.count
let controlPoint2: CGPoint
if d3 < 0.0001 {
controlPoint2 = nextPoint
} else {
controlPoint2 = CGPoint(x: nextPoint.x - (tension * d2 / (d1 + d2)) * (nextNextPoint.x - currentPoint.x),
y: nextPoint.y - (tension * d2 / (d1 + d2)) * (nextNextPoint.y - currentPoint.y))
}
self.path.addCurve(to: nextPoint, controlPoint1: controlPoint1, controlPoint2: controlPoint2)
self.length += nextPoint.distanceFrom(currentPoint)
}
self.path.close()
}
} else if smooth {
let K: CGFloat = 0.2
var c1 = [Int: CGPoint]()
var c2 = [Int: CGPoint]()