KMSyncDot.swift 2.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. //
  2. // KMSyncDot.swift
  3. // PDF Reader Pro
  4. //
  5. // Created by lizhe on 2024/2/29.
  6. //
  7. import Cocoa
  8. import QuartzCore
  9. class KMSyncDot: NSObject {
  10. var point: NSPoint
  11. var page: CPDFPage?
  12. private var phase: Int = 0
  13. private var timer: Timer?
  14. private var handler: ((Bool) -> Void)?
  15. init(point: NSPoint, page: CPDFPage?, updateHandler: ((Bool) -> Void)?) {
  16. self.point = point
  17. self.page = page
  18. self.handler = updateHandler
  19. super.init()
  20. setup()
  21. }
  22. required init?(coder: NSCoder) {
  23. fatalError("init(coder:) has not been implemented")
  24. }
  25. private func setup() {
  26. timer = Timer.scheduledTimer(timeInterval: 0.05, target: self, selector: #selector(animate(_:)), userInfo: nil, repeats: true)
  27. }
  28. deinit {
  29. timer?.invalidate()
  30. handler = nil
  31. }
  32. @objc private func finish(_ timer: Timer) {
  33. timer.invalidate()
  34. if let handler = handler {
  35. handler(true)
  36. }
  37. }
  38. @objc private func animate(_ timer: Timer) {
  39. phase += 1
  40. if phase >= 10 {
  41. timer.invalidate()
  42. self.timer = Timer.scheduledTimer(timeInterval: 5.0, target: self, selector: #selector(finish(_:)), userInfo: nil, repeats: false)
  43. }
  44. if let handler = handler {
  45. handler(false)
  46. }
  47. }
  48. func invalidate() {
  49. timer?.invalidate()
  50. }
  51. func bounds() -> NSRect {
  52. return KMRectFromCenterAndSquareSize(center: point, size: 22.0) ?? CGRectZero
  53. }
  54. func draw(in ctx: CGContext) {
  55. ctx.saveGState()
  56. var s: CGFloat = 6.0
  57. OSMemoryBarrier()
  58. if phase < 10 {
  59. s += 8.0 * sin(CGFloat(phase) * 0.1 * .pi)
  60. let components: [CGFloat] = [1.0, 0.3, 0.3, 1.0, 1.0, 0.0, 0.0, 1.0]
  61. let locations: [CGFloat] = [0.0, 1.0]
  62. let colorspace = CGColorSpaceCreateDeviceRGB()
  63. if let gradient = CGGradient(colorSpace: colorspace, colorComponents: components, locations: locations, count: 2) {
  64. ctx.addEllipse(in: CGRect(x: point.x - 0.5 * s, y: point.y - 0.5 * s, width: s, height: s))
  65. ctx.clip()
  66. ctx.drawRadialGradient(gradient, startCenter: CGPoint(x: point.x, y: point.y + 0.35 * s), startRadius: 0.0, endCenter: CGPoint(x: point.x, y: point.y), endRadius: 0.5 * s, options: [])
  67. }
  68. } else {
  69. ctx.setBlendMode(.multiply)
  70. ctx.setFillColor(CGColor(red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0))
  71. ctx.fillEllipse(in: CGRect(x: point.x - 0.5 * s, y: point.y - 0.5 * s, width: s, height: s))
  72. }
  73. ctx.restoreGState()
  74. }
  75. }