| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458 | 
							- //
 
- //  PQStuckPointCuttingView.swift
 
- //  PQSpeed
 
- //
 
- //  Created by SanW on 2021/5/8.
 
- //  Copyright © 2021 BytesFlow. All rights reserved.
 
- //
 
- import UIKit
 
- import BFCommonKit
 
- class PQStuckPointCuttingView: UIView {
 
-     // 视频时长
 
-     var videoDuration: CGFloat = 0
 
-     var lastVideoDuration:CGFloat = 0
 
-     // 卡点开始时间 默认 0
 
-     var stuckPointStartTime: CGFloat = 0
 
-     // 卡点结束时间
 
-     var stuckPointEndTime: CGFloat = 0
 
-     // 裁剪开始时间 默认 0
 
-     private var cutStartTime: CGFloat = 0
 
- //    /// 裁剪结束最终的开始时间
 
- //    private var cutFinishedStartTime: CGFloat {
 
- //        (scrollView.contentOffset.x / perSecondWidth) + (((scrollView.frame.width - videoCropView.frame.width) / 2 + 15) / perSecondWidth) + cutStartTime
 
- //    }
 
- //
 
- //    /// 裁剪结束最终的结束时间
 
- //    private var cutFinishedEndTime: CGFloat {
 
- //        cutFinishedStartTime + (cutEndTime - cutStartTime)
 
- //    }
 
-     // 播放进度
 
-     private var videoProgress: CGFloat = 0
 
-     // 最小时长 默认 10s
 
-     private var minCutTime: CGFloat = 10
 
-     /// 时间间隔
 
-     private var timeRange: CGFloat = cDefaultMargin
 
-     /// 刻度高
 
-     private var rateHeight: CGFloat = 23
 
-     /// 时间线宽
 
-     private var timeLineWidth: CGFloat = 35
 
-     /// 时间线间隔
 
-     private var timeLineMargin: CGFloat = 35
 
-     /// 时间线高
 
-     private var timeHeight: CGFloat = cDefaultMargin * 4
 
-     /// 频率线宽
 
-     private var frequencyWidth: CGFloat =  adapterWidth(width: 1.5)
 
-     /// 频率间隔
 
-     private var frequencyMargin: CGFloat =  adapterWidth(width: 3)
 
-     /// 竖线和contentview 父视图的左右间隔
 
-     private var margin: CGFloat = (cScreenWidth - adapterWidth(width: 250)) / 2
 
-     /// 滑动区域大小
 
-     private var contentWidth: CGFloat = 0
 
-     // 竖线一个间隔代表多少 S 是动态的
 
-     private var oneMarginTime: CGFloat = 0
 
-     private var isDrawLine: Bool = false
 
-     // 保存已经绘制的竖线用于变色使用
 
-     var lineLayerArray: Array = Array<CAShapeLayer>.init()
 
-     var lastDrawedLineIndex : Int = 0
 
-     // 裁剪区的相素大小
 
-     var cropViewWidth: CGFloat = adapterWidth(width: 250)
 
-     /// 拖拽改变实时的回调
 
-     var videoRangeDidChanged: ((_ startTime: CGFloat, _ endTime: CGFloat) -> Void)?
 
-     /// 进度改变实时的回调
 
-     var videoProgressDidChanged: ((_ progress: CGFloat) -> Void)?
 
-     /// 拖缀结束的回调 type - 1-拖动左边裁剪结束 2--拖动右边裁剪结束 3-进度条拖动结束 4-滑动结束
 
-     var videoDidEndDragging: ((_ type: Int, _ startTime: CGFloat, _ endTime: CGFloat, _ progress: CGFloat) -> Void)?
 
-     // 开始划动
 
-     var videoDidBeginDrag: (() -> Void)?
 
-     // 选择区内的线个数
 
-     var wavSelectCount: Int = 0
 
-     // 整首歌的线的个数
 
-     var wavTotalCount: Int = 0
 
-     
 
-     //推荐虚线的位置
 
-     var startLineX:CGFloat = 0.0
 
-     
 
-     //如果是用户主动划动的 就不自动滚动到推荐位置了
 
-     var isUserDrag:Bool = false
 
-     // 推荐卡点起始时间
 
-     var suggestRhythmStartTime:CGFloat = 0.0
 
-     var suggestRhythmEndTime:CGFloat = 0.0
 
-     
 
-     /// 滚动视图
 
-     lazy var scrollView: UIScrollView = {
 
-         let scrollView = UIScrollView(frame: bounds)
 
-         scrollView.showsVerticalScrollIndicator = false
 
-         scrollView.showsHorizontalScrollIndicator = false
 
-         scrollView.bounces = false
 
-         scrollView.delegate = self
 
-         if #available(iOS 11.0, *) {
 
-             scrollView.contentInsetAdjustmentBehavior = .never
 
-         } else {
 
- //            automaticallyAdjustsScrollViewInsets = false
 
-         }
 
-         scrollView.backgroundColor = .clear
 
-         return scrollView
 
-     }()
 
-     //
 
-     lazy var rateView: UIView = {
 
-         let rateView = UIView(frame: CGRect(x: 0, y: 22, width: scrollView.contentSize.width, height: rateHeight))
 
-         rateView.backgroundColor = .clear
 
-         return rateView
 
-     }()
 
-     // 总时长
 
-     lazy var tatalTimeLabel: UILabel = {
 
-         let tatalTimeLabel = UILabel()
 
-         tatalTimeLabel.font = UIFont.systemFont(ofSize: 11)
 
-         tatalTimeLabel.textAlignment = .right
 
-         tatalTimeLabel.tag = 66
 
-         tatalTimeLabel.textColor = UIColor.hexColor(hexadecimal: PQBFConfig.shared.styleColor.rawValue)
 
-         return tatalTimeLabel
 
-     }()
 
-     // 显示选择框
 
-     lazy var videoCropView: UIView = {
 
-         let videoCropView: UIView = UIView(frame: CGRect(x: (cScreenWidth - cropViewWidth) / 2, y: 0, width: cropViewWidth, height: 80))
 
-         videoCropView.isUserInteractionEnabled = false
 
-         videoCropView.layer.borderColor = UIColor.hexColor(hexadecimal: PQBFConfig.shared.styleColor.rawValue).cgColor
 
-         videoCropView.layer.borderWidth = 2
 
-         videoCropView.layer.cornerRadius = 8
 
-         return videoCropView
 
-     }()
 
-     //两边的mask 2 是裁剪区的边框
 
-     lazy var leftMaskView: UIView = {
 
-         let leftMaskView: UIView = UIView(frame: CGRect(x:0, y: 0, width: (cScreenWidth - cropViewWidth) / 2 - 2, height: 80))
 
-         leftMaskView.backgroundColor = UIColor.white
 
-         leftMaskView.alpha = 0.7
 
-         return leftMaskView
 
-     }()
 
-     
 
-     //右边的mask 2 是裁剪区的边框
 
-     lazy var rightMaskView: UIView = {
 
-         let rightMaskView: UIView = UIView(frame: CGRect(x:videoCropView.frame.maxX + 2, y: 0, width: (cScreenWidth - cropViewWidth) / 2, height: 80))
 
-         rightMaskView.backgroundColor = UIColor.white
 
-         rightMaskView.alpha = 0.7
 
-         return rightMaskView
 
-     }()
 
-     
 
-     
 
-     private override init(frame: CGRect) {
 
-         super.init(frame: frame)
 
-     }
 
-     required init?(coder _: NSCoder) {
 
-         fatalError("init(coder:) has not been implemented")
 
-     }
 
-     init(frame: CGRect, duration: CGFloat, suggestRhythmStartTime: CGFloat) {
 
-         super.init(frame: frame)
 
-         videoDuration = duration
 
-         self.suggestRhythmStartTime = suggestRhythmStartTime
 
-     
 
-     }
 
-     /// 更新卡点值
 
-     /// - Parameter endTime: endTime description
 
-     /// - Returns: <#description#>
 
-     func updateEndTime(startTime: CGFloat, endTime: CGFloat,
 
-                        suggestRhythmStartTime: CGFloat, suggestRhythmEndTime: CGFloat) {
 
-         
 
- //        videoDuration = duration
 
-         self.suggestRhythmStartTime = suggestRhythmStartTime
 
-         self.suggestRhythmEndTime = suggestRhythmEndTime
 
-         startLineX = 0
 
-         
 
-         stuckPointStartTime = startTime
 
-         stuckPointEndTime = endTime
 
-         
 
-         tatalTimeLabel.text = "\(Float64(stuckPointEndTime - stuckPointStartTime).formatDurationToHMS())"
 
-   
 
-         BFLog(1, message: "播放开始:\(stuckPointStartTime) 结束:\(stuckPointEndTime) 时长为:\(stuckPointEndTime - stuckPointStartTime); 音乐总时长为:\(videoDuration);推荐卡点开始:\(suggestRhythmStartTime) 结束:\(suggestRhythmEndTime)")
 
-         backgroundColor = PQBFConfig.shared.styleBackGroundColor
 
-         addSubview(scrollView)
 
-         addSubview(videoCropView)
 
-         addSubview(leftMaskView)
 
-         addSubview(rightMaskView)
 
-         videoCropView.addSubview(tatalTimeLabel)
 
-         addData()
 
-         videoCropView.frame = CGRect(x: (cScreenWidth - cropViewWidth) / 2, y: 0, width: cropViewWidth, height: 80)
 
-         leftMaskView.frame = CGRect(x:0, y: 0, width: (cScreenWidth - cropViewWidth) / 2 - 2, height: 80)
 
-         rightMaskView.frame = CGRect(x:videoCropView.frame.maxX + 2, y: 0, width: (cScreenWidth - cropViewWidth) / 2 - 2, height: 80)
 
-         tatalTimeLabel.snp.remakeConstraints { make in
 
-             make.width.equalTo(40)
 
-             make.height.equalTo(15)
 
-             make.top.equalTo(videoCropView.snp_top).offset(6)
 
-             make.right.equalTo(videoCropView.snp_right).offset(-6)
 
-         }
 
-         
 
-       
 
-     }
 
-     func addData() {
 
-         // 1,选择区内的线个数 ,划动区域后 个数会变???
 
-         wavSelectCount = Int(ceil((adapterWidth(width: 250) - frequencyWidth) / (frequencyWidth + frequencyMargin)) + 1)
 
-         
 
-         cropViewWidth = CGFloat(wavSelectCount) * (frequencyWidth + frequencyMargin) + frequencyWidth
 
-         margin = (cScreenWidth - cropViewWidth) / 2.0
 
-      
 
-         // 2竖线一个间隔代表多少 S 是动态的
 
-         oneMarginTime = (stuckPointEndTime - stuckPointStartTime) / CGFloat(wavSelectCount)
 
-         
 
-         // 如果视频结束时间点大于歌曲有效结束点,则拼接推荐的时间段直到满足视频播放
 
-         var videoDurationTemp = suggestRhythmEndTime
 
-         while stuckPointEndTime > videoDurationTemp {
 
-             videoDurationTemp  += (suggestRhythmEndTime - suggestRhythmStartTime)
 
-         }
 
-         videoDuration = videoDurationTemp
 
-         // 3,一共绘制的竖线个数
 
-         wavTotalCount = Int(ceil(videoDuration / oneMarginTime) + 1)
 
-         timeRange = oneMarginTime * 10
 
-         // 显示时间 label 的个数 , -1 不够整倍数就不显示时间了
 
-         let timeLabelCount = Int(wavTotalCount / 10)
 
-         
 
-         contentWidth = CGFloat(wavTotalCount - 1) * (frequencyWidth + frequencyMargin) + frequencyWidth + (cScreenWidth - cropViewWidth)
 
-         if contentWidth < scrollView.frame.width {
 
-             contentWidth = scrollView.frame.width
 
-         }
 
-         scrollView.contentSize = CGSize(width: contentWidth, height: scrollView.frame.height)
 
-         
 
-         BFLog(1, message: "框内个数:\(wavSelectCount), 总线条数:\(wavTotalCount), 框宽:\(cropViewWidth), 最终音乐时长:\(videoDuration)")
 
-         scrollView.subviews.forEach { lable in
 
-             if lable is UILabel && lable.tag != 66 {
 
-                 lable.removeFromSuperview()
 
-             }
 
-         }
 
-         for index in 0 ... timeLabelCount {
 
- //            scrollView.viewWithTag(100 + index)?.removeFromSuperview()
 
-             let titleLab = UILabel(frame: CGRect(x: CGFloat(index) *  (frequencyWidth + frequencyMargin) * 10 + margin - timeLineWidth / 2, y: rateView.frame.maxY, width: timeLineWidth, height: 30))
 
-             titleLab.font = UIFont.systemFont(ofSize: 11)
 
-             titleLab.textAlignment = .center
 
-             titleLab.numberOfLines = 1
 
-             titleLab.tag = 100 + index
 
-             titleLab.backgroundColor = .clear
 
-             titleLab.textColor = UIColor.hexColor(hexadecimal: "#999999")
 
-           
 
-             titleLab.text = "\(Float64(Int(CGFloat(index) * timeRange)).formatDurationToHMS())"
 
-             scrollView.addSubview(titleLab)
 
-         }
 
-         if oneMarginTime > 0 {
 
-             // 1,处理音频频率
 
-             configVoiceFrequency()
 
-             // 2,滚动到推荐位置
 
-             if(!isUserDrag){
 
-                 scrollView.contentOffset = CGPoint(x: startLineX - margin, y: 0)
 
-             }
 
-         
 
-             scrollView.addSubview(rateView)
 
-         }
 
-     }
 
-     /// 处理音频频率
 
-     /// - Returns: <#description#>
 
-     func configVoiceFrequency() {
 
-         // 整倍数
 
-         let waveTotalCount = Int(wavTotalCount) / cFrequency.count
 
-         // 余多少个未画的
 
-         var remainder = Int(wavTotalCount % cFrequency.count)
 
-         var totalWave: [CGFloat] = Array<CGFloat>.init()
 
- //         1,先画整倍数个竖线
 
-         for _ in 0 ..< waveTotalCount {
 
-             totalWave = totalWave + cFrequency
 
-         }
 
-         if remainder > cFrequency.count - 1 {
 
-             remainder = cFrequency.count - 1
 
-         }
 
-         // 1,再画余数个竖线
 
-         if remainder > 0 {
 
-             totalWave = totalWave + cFrequency[0 ... (remainder - 1)]
 
-         }
 
-         createWave(waveArr: totalWave)
 
-     }
 
-     /// 更新进度绘制不同色值
 
-     /// progress <#progress description#>
 
-     func updateProgress(progress: CGFloat) {
 
-         
 
-         if(progress <= 0 || lineLayerArray.count == 0 || progress.isNaN){
 
-             BFLog(message: "progress is error ")
 
-             return
 
-         }
 
-         
 
-         let startIndex = scrollView.contentOffset.x / (frequencyWidth + frequencyMargin)
 
-         lastDrawedLineIndex = max(lastDrawedLineIndex, Int(ceil(startIndex)))
 
-         let selectIndex = Int(ceil(startIndex + progress * CGFloat(wavSelectCount)))
 
-         while(selectIndex < lineLayerArray.count && selectIndex > lastDrawedLineIndex){
 
-             let drawLayer:CAShapeLayer = lineLayerArray[lastDrawedLineIndex]
 
-             if drawLayer.strokeColor != UIColor.hexColor(hexadecimal: PQBFConfig.shared.styleColor.rawValue).cgColor{
 
- //                BFLog(1, message: "progress is \(progress) i \(lastDrawedLineIndex) 命中的位置:\(CGFloat(lastDrawedLineIndex) * oneMarginTime)")
 
-                 drawLayer.strokeColor = UIColor.hexColor(hexadecimal: PQBFConfig.shared.styleColor.rawValue).cgColor
 
-                 drawLayer.setNeedsDisplay()
 
-                 drawLayer.layoutIfNeeded()
 
-             }
 
-             lastDrawedLineIndex += 1
 
-         }
 
-         
 
-         if(progress >= 0.999){
 
-             BFLog(message: "播放完成 重新更新 UI ")
 
-             resetDefaultsColor(clearData: false)
 
-         }
 
-     }
 
-     // 竖线恢复到原有色值
 
-     func resetDefaultsColor(clearData:Bool = true) {
 
-         lastDrawedLineIndex = 0
 
-         for layer in lineLayerArray {
 
-             layer.strokeColor = UIColor.hexColor(hexadecimal: "#999999").cgColor
 
-             layer.setNeedsDisplay()
 
-         }
 
-         if(clearData == true){
 
-             lineLayerArray.removeAll()
 
-             if(rateView.layer.sublayers != nil){
 
-                 for (_,layer) in rateView.layer.sublayers!.enumerated() {
 
-                     layer.removeFromSuperlayer()
 
-                 }
 
-             }
 
-       
 
-             isUserDrag = false
 
-             isDrawLine = false
 
-         }
 
-    
 
-     }
 
-     /// 生成波纹
 
-     /// - Parameter waveArr: <#waveArr description#> // warning 有崩溃 _buffer    _ArrayBuffer<CoreGraphics.CGFloat>    wavearr为CoreGraph.CGFloat数组
 
-     /// - Returns: <#description#>
 
-     func createWave(waveArr: [CGFloat]) {
 
-         for (i, power) in waveArr.enumerated() {
 
-             // 画布高度
 
-             let hight: CGFloat = rateView.frame.height
 
-             // 开始 Y 值
 
-             var startY: CGFloat = (hight - power) / 2.0
 
-             if startY < 0 { startY = 0 }
 
-             // 结束 Y 值
 
-             var endY: CGFloat = startY + power
 
-             if endY > CGFloat(hight) { endY = hight }
 
-             // 线的路径
 
-             let linePath = UIBezierPath()
 
-             // 起点 timeLineWidth / 2 处理显示时间的 label中心为时间点
 
-             let originX: CGFloat = CGFloat(Float(i) * Float(frequencyWidth + frequencyMargin)) + margin
 
-             linePath.move(to: CGPoint(x: originX, y: startY))
 
-             // 终点
 
-             linePath.addLine(to: CGPoint(x: originX, y: endY))
 
-             let lineLayer = CAShapeLayer()
 
-             lineLayer.lineWidth = frequencyWidth
 
-             lineLayer.strokeColor = UIColor.hexColor(hexadecimal: "#999999").cgColor
 
-             lineLayer.path = linePath.cgPath
 
-             lineLayer.fillColor = UIColor.black.cgColor
 
-             // 推荐的开始起点是虚线 减0.0001因为精度问题
 
- //            BFLog(1, message: "suggestRhythmStartTime is \(suggestRhythmStartTime)")
 
-             if oneMarginTime * CGFloat(i) >= (suggestRhythmStartTime-0.0001) && !isDrawLine {
 
-                 isDrawLine = true
 
-                 linePath.move(to: CGPoint(x: originX, y: -10))
 
-                 // 终点
 
-                 linePath.addLine(to: CGPoint(x: originX, y: 30))
 
-                 lineLayer.path = linePath.cgPath
 
-                 lineLayer.lineDashPhase = 0
 
-                 lineLayer.lineDashPattern = [3, 3]
 
-             }
 
-             if startLineX == 0 && oneMarginTime * CGFloat(i) >= stuckPointStartTime{
 
-                 startLineX = originX
 
-             }
 
-             lineLayerArray.append(lineLayer)
 
-             rateView.layer.insertSublayer(lineLayer, at: 0)
 
-         }
 
-     }
 
-     deinit {
 
-         BFLog(message: "卡点裁剪-裁剪视图销毁")
 
-     }
 
-     
 
-     //划动结速后处理
 
-     func moveEnd() {
 
-         //最后一个竖线VIEW
 
-         let lastLine:UIView = scrollView.viewWithTag(100 +  Int(videoDuration / timeRange) - 1) ?? UIView.init()
 
-         //移动后的开始时间
 
-         let startTime =  videoDuration / lastLine.frame.maxX * scrollView.contentOffset.x
 
- //        let startTime =  videoDuration * (margin + scrollView.contentOffset.x) / scrollView.contentSize.width
 
-         //选中的时长
 
-         let selectDuration:CGFloat = CGFloat(stuckPointEndTime - stuckPointStartTime)
 
-         BFLog(message: "拖拽结束 - 回调\(scrollView.contentOffset)  \(scrollView.contentSize) 开始时间为:\(startTime) 结束时间为:\(startTime + selectDuration)")
 
-         stuckPointStartTime = startTime
 
-         stuckPointEndTime = stuckPointStartTime + selectDuration
 
-         if(videoDidEndDragging != nil){
 
-             videoDidEndDragging!(1,startTime,startTime + CGFloat(stuckPointEndTime - stuckPointStartTime),0)
 
-         }
 
-         resetDefaultsColor(clearData: false)
 
-         PQEventTrackViewModel.baseReportUpload(businessType: .bt_buttonClick, objectType: .ot_shanyinApp_musicVideoPreview_musicPeriodSelect, pageSource: .sp_shanyinApp_main, extParams: nil, remindmsg: "")
 
-     }
 
- }
 
- // MARK: - scrollView滑动代理
 
- /// scrollView滑动代理
 
- extension PQStuckPointCuttingView: UIScrollViewDelegate {
 
-     func scrollViewDidScroll(_: UIScrollView) {}
 
-  
 
-     func scrollViewWillBeginDragging(_ :UIScrollView){
 
-         isUserDrag = true
 
-         if(videoDidBeginDrag != nil){
 
-             videoDidBeginDrag!()
 
-         }
 
-         
 
-     }
 
-     
 
-     func scrollViewDidEndDecelerating(_: UIScrollView) {
 
-         if !scrollView.isDragging, !scrollView.isDecelerating {
 
-             DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.2) { [weak self] in
 
-                 self?.moveEnd()
 
-             }
 
-         }
 
-        
 
-     }
 
-     func scrollViewDidEndDragging(_:UIScrollView,willDecelerate decelerate:Bool){
 
-         if !decelerate, !scrollView.isDragging, !scrollView.isDecelerating {
 
-             
 
-             DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.2) { [weak self] in
 
-                 self?.moveEnd()
 
-             }
 
-  
 
-         }
 
-        
 
-     }
 
-     
 
-     func scrollViewDidEndScrollingAnimation(_: UIScrollView) {
 
-         BFLog(message: "scrollViewDidEndScrollingAnimation")
 
-         
 
-         
 
-     }
 
- }
 
 
  |