How to draw a text using CoreGrahpics & Core anima

2020-06-06 05:55发布

问题:

I want to achieve this type of animation.please check this URL

https://www.dropbox.com/s/gz9vfy5refsh7a5/Screen.mov?dl=0

I am adding Code which I have used in the project where I have used Core Graphics to render the text with animation but I am not able to animate text which is being shown in the mentioned link

This is code which i am using to render the text on the screen

    let context = UIGraphicsGetCurrentContext()!
    context.saveGState()
    context.setAlpha(noLightText)

    let nOLIGHTRect = CGRect(x: 145.66, y: 590, width: 449.38, height: 125)
    let nOLIGHTStyle = NSMutableParagraphStyle()
    nOLIGHTStyle.alignment = .center
    let nOLIGHTFontAttributes = [
        .font: UIFont(name: "Futura-CondensedExtraBold", size: 96)!,
        .foregroundColor: nOLIGHTColor,
        .paragraphStyle: nOLIGHTStyle,
        ] as [NSAttributedStringKey: Any]

    "NO LIGHT".draw(in: nOLIGHTRect, withAttributes: nOLIGHTFontAttributes)

    context.restoreGState()


    //// LOW LIGHT Drawing
    context.saveGState()
    context.setAlpha(_0)

    let lOWLIGHTRect = CGRect(x: 145.94, y: 590, width: 449.38, height: 125)
    let lOWLIGHTStyle = NSMutableParagraphStyle()
    lOWLIGHTStyle.alignment = .left
    let lOWLIGHTFontAttributes = [
        .font: UIFont(name: "Futura-CondensedExtraBold", size: 96)!,
        .foregroundColor: lOWLIGHTColor,
        .paragraphStyle: lOWLIGHTStyle,
        ] as [NSAttributedStringKey: Any]

    "LOW LIGHT".draw(in: lOWLIGHTRect, withAttributes: lOWLIGHTFontAttributes)

    context.restoreGState()


    //// MID LIGHT Drawing
    context.saveGState()
    context.setAlpha(mediumLightText)

    let mIDLIGHTRect = CGRect(x: 145.77, y: 590, width: 449.38, height: 125)
    let mIDLIGHTStyle = NSMutableParagraphStyle()
    mIDLIGHTStyle.alignment = .left
    let mIDLIGHTFontAttributes = [
        .font: UIFont(name: "Futura-CondensedExtraBold", size: 96)!,
        .foregroundColor: mIDLIGHTColor,
        .paragraphStyle: mIDLIGHTStyle,
        ] as [NSAttributedStringKey: Any]


    "MID LIGHT".draw(in: mIDLIGHTRect, withAttributes: mIDLIGHTFontAttributes)

    context.restoreGState()

回答1:

If you want to animate the outline strokes of multiple characters at once you can:

  • determine the CGPaths for each character
  • map the CGPaths to CAShapeLayers
  • animate the strokeEnd property

This would look like this:

Please note: The fonts used by iOS are outline fonts using either the TrueType or OpenType format. That means that each glyph is using lines and curves to define its boundary. What we see normally when a glyph is rendered is the filled area. Therefore if the 'strokeEnd' property is animated then the outline of the glyph is drawn. The following code shows how this can be achieved.

Animation of the text in a way that a human being would write the text would be more effort (e. g. using a stroke font instead of an outline font plus associated engine or using user-defined vector paths representing each character)

var charLayers = [CAShapeLayer]()

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)

    for layer in self.charLayers {
        layer.removeFromSuperlayer()
    }

    let stringAttributes = [ NSAttributedStringKey.font: UIFont(name: "Futura-CondensedExtraBold", size: 64.0)! ]
    let attributedString = NSMutableAttributedString(string: "hello world", attributes: stringAttributes )
    let charPaths = self.characterPaths(attributedString: attributedString, position: CGPoint(x: 24, y: 192))

    self.charLayers = charPaths.map { path -> CAShapeLayer in
        let shapeLayer = CAShapeLayer()
        shapeLayer.fillColor = UIColor.clear.cgColor
        shapeLayer.strokeColor = UIColor.red.cgColor
        shapeLayer.lineWidth = 2
        shapeLayer.path = path
        return shapeLayer
    }

    for layer in self.charLayers {
        view.layer.addSublayer(layer)
        let animation = CABasicAnimation(keyPath: "strokeEnd")
        animation.fromValue = 0
        animation.duration = 2.2
        layer.add(animation, forKey: "charAnimation")
    }
}

func characterPaths(attributedString: NSAttributedString, position: CGPoint) -> [CGPath] {

    let line = CTLineCreateWithAttributedString(attributedString)

    guard let glyphRuns = CTLineGetGlyphRuns(line) as? [CTRun] else { return []}

    var characterPaths = [CGPath]()

    for glyphRun in glyphRuns {
        guard let attributes = CTRunGetAttributes(glyphRun) as? [String:AnyObject] else { continue }
        let font = attributes[kCTFontAttributeName as String] as! CTFont

        for index in 0..<CTRunGetGlyphCount(glyphRun) {
            let glyphRange = CFRangeMake(index, 1)

            var glyph = CGGlyph()
            CTRunGetGlyphs(glyphRun, glyphRange, &glyph)

            var characterPosition = CGPoint()
            CTRunGetPositions(glyphRun, glyphRange, &characterPosition)
            characterPosition.x += position.x
            characterPosition.y += position.y

            if let glyphPath = CTFontCreatePathForGlyph(font, glyph, nil) {
                var transform = CGAffineTransform(a: 1, b: 0, c: 0, d: -1, tx: characterPosition.x, ty: characterPosition.y)
                if let charPath = glyphPath.copy(using: &transform) {
                    characterPaths.append(charPath)
                }
            }
        }
    }
    return characterPaths
}