[Swift3] Cách tạo hiệu ứng viết chữ chuyên nghiệp
Bài đăng này đã không được cập nhật trong 3 năm
Chào các bạn, hôm nay mình sẽ trở lại với chủ đề tạo hiệu ứng viết chữ chuyện nghiệp, được sử dụng trong các app học hán tự, tiếng nhật... trước hết mời các bạn xem topic này sẽ hướng dẫn bạn làm được gì:
Vậy chúng ta cần phân tích 1 chút: theo như hình ảnh trên thì chúng ta sẽ cần 2 layer, 1 layer dùng để tạo các nét chữ mờ bên dưới, layer còn lại sẽ sử dụng làm hiệu ứng, kết hợp với animation, mỗi nét cách nhau 1s tiếp theo sẽ có yêu cầu nữa là khi tap vào chữ nó sẽ replay lại 1 lần nữa animation này. Let's Start:
Bước 1: Khởi tạo project TextAnimationExample và khởi tạo pod thông qua câu lệnh pod init add thêm pod SVGPath, trường hợp này chúng ta sẽ sử dụng lib SVGPath để xử lý các nét vẽ: pod "SVGPath" tiếp gõ lệnh pod install và mở file xcode workspace, ok sang bước 2
Bước 2: Tạo file SVGAnimationView.swift và khởi tạo các dòng code ban đầu như sau:
var animatedLayers: [CAShapeLayer] = []
var animationPaths: [UIBezierPath] = []
lazy var shapeLayer: CAShapeLayer = {
let path = UIBezierPath(roundedRect: self.bounds, cornerRadius: 2)
let shapeLayer = CAShapeLayer()
shapeLayer.path = path.cgPath
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.strokeColor = UIColor.black.cgColor
shapeLayer.lineWidth = 1.0
return shapeLayer
}()
var animationCount = 0
override func awakeFromNib() {
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(replayAnimation))
self.addGestureRecognizer(tapGesture)
self.clipsToBounds = false
self.layer.shadowColor = UIColor.black.cgColor
self.layer.shadowOffset = CGSize(width: 1, height: 1)
self.layer.shadowRadius = 5
}
Chúng ta sẽ sử dụng phương thức awakeFromNib hỗ trợ cho việc kéo thả trong nib hoặc storyboard
Bước 3: Xử lý animation và animation path
func replayAnimation() {
if animationCount == animatedLayers.count {
playAnimation(0)
}
}
func playAnimation(_ delay: TimeInterval) {
var delay = delay
if animationPaths.count == animatedLayers.count {
animationCount = 0
for index in 0 ..< animatedLayers.count {
let layer = animatedLayers[index]
let path = animationPaths[index]
layer.removeFromSuperlayer()
self.perform(#selector(startAnimationLayer(_:)), with: [layer, path], afterDelay: delay, inModes: [RunLoopMode.commonModes])
delay += 0.8
}
}
}
func stopAnimations() {
self.layer.removeAllAnimations()
NSObject.cancelPreviousPerformRequests(withTarget: self)
}
func playAnimationFromPath(_ s_Paths: String?) {
if let s_Paths = s_Paths {
let paths = s_Paths.components(separatedBy: "|")
for path in paths {
let layer = CAShapeLayer()
let bezierPath = UIBezierPath(svgPath: path)
let layerBG = CAShapeLayer()
layerBG.fillColor = UIColor.clear.cgColor
layerBG.strokeColor = UIColor.black.withAlphaComponent(0.2).cgColor
layerBG.path = bezierPath.cgPath
layerBG.lineWidth = 6
self.layer.addSublayer(layerBG)
layer.lineWidth = 6
layer.path = bezierPath.cgPath
layer.fillColor = UIColor.clear.cgColor
layer.strokeColor = UIColor.black.cgColor
animatedLayers.append(layer)
animationPaths.append(bezierPath)
}
}
playAnimation(1)
}
playAnimationFromPath: Phương thức này sẽ tạo ra 2 layer, 1 cái nằm bên dưới mờ, không animation, và 1 layer animation ở bên trên playAnimation: Phương thức này sẽ xử lý animation trên main thread, các path sẽ được đọc từ mảng ở phương thức trên và sử dụng 1 layer để vẽ
Bước 4: Thêm CAAnimationDelegate và implement funtion bên dưới để có thể xử lý vẽ cho từng nét đặt value từ 0 đến 1 nhé
func addAnimationToLayer(_ layer: CAShapeLayer, duration: TimeInterval) {
let animation = CABasicAnimation(keyPath: "strokeEnd")
animation.delegate = self
animation.fromValue = 0
animation.toValue = 1
animation.duration = duration
layer.add(animation, forKey: nil)
}
Vậy là xong, đây là phương pháp khá đơn giản nhưng cũng cực kỳ hiệu quả, các bạn có thể sáng tạo thêm nữa nhé, chúc các bạn tạo được nhiều app học tiếng nhật, kanji hay từ sự hỗ trợ của topic này.
Source code cho các bạn tham khảo https://github.com/vanthanh88/TextAnimationExample
All rights reserved