React-Native画线平滑处理

Posted 苏小败在路上

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了React-Native画线平滑处理相关的知识,希望对你有一定的参考价值。

参考:http://blog.csdn.net/pz789as/article/details/52795275


这次开发要手写画线,我们一般画线的时候是直接获取屏幕上的点,然后利用ART绘制出一天路径线:

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 * @flow
 */

import React,  Component  from 'react';
import 
  StyleSheet,
  Text,
  View,
  PanResponder,
  TouchableOpacity,
  StatusBar,
  ART,
 from 'react-native';

const 
  Shape,
  Group,
  Transform,
  Surface,
  Path,
  Pattern,
  LinearGradient,
  RadialGradient,
  // Text,
  ClippingRectangle,
 = ART;

import Utils from './Utils';

let cv = 
  status_norm: 0,
  status_auto: 1,
  status_pause: 2,

  touch_begin: 0,
  touch_move: 1,
  touch_ended: 2,
;

export default class DrawLayout extends Component 
  constructor(props)
    super(props);
    this._panResponder = ;
    this.mousePosition = null;
    this.lastMousePostion = null;
    this.arrOrgPoint = [];
    this.arrUsedPoint = [];
    this.nowR = 10;
    this.blnCanDraw = false;
    this.showPoints = null;

    this.status = cv.status_norm;
    this.wrongCount = 0;
    this.state=
      blnUpdate: false,
    ;
  
  setUpdate()
    this.setState(
      blnUpdate: !this.state.blnUpdate,
    );
  
  componentWillMount() 
    this._panResponder = PanResponder.create(
      onStartShouldSetPanResponder: this.onStartShouldSetPanResponder.bind(this),
      onMoveShouldSetPanResponder: this.onMoveShouldSetPanResponder.bind(this),
      onPanResponderGrant: this.onPanResponderGrant.bind(this),
      onPanResponderMove: this.onPanResponderMove.bind(this),
      onPanResponderRelease: this.onPanResponderRelease.bind(this),
      onPanResponderTerminate: this.onPanResponderTerminate.bind(this),
    );
  
  componentDidMount() 
    this._autoUpdate = setInterval(this.autoUpdate.bind(this), 1/60);
  
  componentWillUnmount() 
    this._autoUpdate && clearInterval(this._autoUpdate);
  
  
  onStartShouldSetPanResponder(e, g)
    if (this.status == cv.status_auto || this.status == cv.status_pause)
      return false;
    
    return true;
  
  onMoveShouldSetPanResponder(e, g)
    if (this.status == cv.status_auto || this.status == cv.status_pause)
      return false;
    
    return true;
  
  onPanResponderGrant(e, g)
    if (g.numberActiveTouches == 1)
      this.mousePosition = 
        x: e.nativeEvent.locationX,
        y: e.nativeEvent.locationY
      ;
      this.ResetDrawPoint();
      this.AddUsePoint(this.mousePosition, cv.touch_begin);
    
  
  onPanResponderMove(e, g)
    if (g.numberActiveTouches == 1)
      this.mousePosition = 
        x: e.nativeEvent.locationX,
        y: e.nativeEvent.locationY
      ;
      var s = Utils.DisP(this.mousePosition, this.lastMousePostion);
      if (s >= 1)
        this.AddUsePoint(this.mousePosition, cv.touch_move);
      
    
  
  onPanResponderRelease(e, g)
    this.endPanResponder(e, g);
  
  onPanResponderTerminate(e, g)
    this.endPanResponder(e, g);
  
  endPanResponder(e, g)
    this.mousePosition = 
      x: e.nativeEvent.locationX,
      y: e.nativeEvent.locationY
    ;
    this.AddUsePoint(this.mousePosition, cv.touch_ended);
  
  ResetDrawPoint()
    this.arrOrgPoint = [];
    this.arrUsedPoint = [];
    this.nowR = 5;
    this.blnCanDraw = false;
    this.showPoints = null;
  
  AddUsePoint(pos, kind)
    if (kind == cv.touch_begin)
      this.lastMousePostion = this.mousePosition;
      this.arrOrgPoint.push(pos);
      this.AddSinglePoint(pos, this.nowR);
      this.blnCanDraw = true;
    else if (this.blnCanDraw)
      this.arrOrgPoint.push(pos);
      var blnSet = false;
      if (this.arrOrgPoint.length > 2)
        var count = Utils.DisP(this.lastMousePostion, pos);
        if (count > 1)
          for(var i=0;i<count;i++)
            var p = Utils.LerpP(this.lastMousePostion, pos, (i+1)/count);
            this.AddSinglePoint(p, this.nowR);
          
        
        blnSet = true;
      else
        var count = Utils.DisP(this.lastMousePostion, pos);
        if (count > 1)
          var c = Math.ceil(count);
          for(var i=0; i < c; i++)
            if (i == c - 1)
              this.AddSinglePoint(pos, this.nowR);
            else
              var p = Utils.LerpP(this.lastMousePostion, pos, (i + 1) / c);
              this.AddSinglePoint(p, this.nowR);
            
          
          blnSet = true;
        else 
          // this.AddSinglePoint(pos, this.nowR);
          blnSet = false;
        
      
    
    if (kind == cv.touch_ended)
      // this.ResetDrawPoint();
    
    if (blnSet)
      this.lastMousePostion = this.mousePosition;
    
    this.setUpdate();
  
  AddSinglePoint(pos, r)
    this.arrUsedPoint.push(pos);
    d = new Path();
    for(var i=0;i<this.arrUsedPoint.length;i++)
      var p = this.arrUsedPoint[i];
      if (i==0)
        d.moveTo(p.x, p.y);
      else
        d.lineTo(p.x, p.y);
      
    
    this.showPoints = d;
  
  render() 
    return (
      <View style=styles.container ...this._panResponder.panHandlers>
        <View style=styles.mouseView>
          <Surface ref='lineView' width=ScreenWidth height=ScreenHeight>
            <Shape d=this.showPoints stroke='rgb(0,0,255)' strokeWidth=this.nowR />
          </Surface>
        </View>
      </View>
    );
  

这里主要是在AddUsePoint里面对触摸点进行处理,在touchmove之后,每次都要保存好获得到的点,然后取差值,进行计算

上面代码运行后,得到的线条是这样的:


看到结果后,是不是很不理想,我们想要的不是横平竖直啊,怎么使它们变得平滑呢,我开始想的是贝塞尔曲线,于是在网上找,终于找到一个算法,叫B样条曲线算法,其实这个就是贝塞尔的一种,原理大概就是根据其中几个点进行一个公式算法,得到这些点之间的其他点。

于是在上面的代码进行改善,修改AddUsePoint,添加B样条曲线算法,先看看效果:

       

效果很不错哦~

代码如下:

AddUsePoint(pos, kind)
    if (kind == cv.touch_begin)
      this.lastMousePostion = this.mousePosition;
      this.arrOrgPoint.push(pos);
      this.AddSinglePoint(pos, this.nowR);
      this.blnCanDraw = true;
    else if (this.blnCanDraw)
      this.arrOrgPoint.push(pos);
      var blnSet = false;
      if (this.arrOrgPoint.length > 2)
        var listTemp = [];//将最新的三个点加入一个临时数组里面
        listTemp.push(this.arrOrgPoint[this.arrOrgPoint.length - 3]);
        listTemp.push(this.arrOrgPoint[this.arrOrgPoint.length - 2]);
        listTemp.push(this.arrOrgPoint[this.arrOrgPoint.length - 1]);
        listTemp = this.BSpline2Smooth1(listTemp, false);//将数组传入算法中进行计算
        for(var i=0;i<listTemp.length;i++)
          this.AddSinglePoint(listTemp[i], this.nowR);
        
        if (this.arrUsedPoint.length > 500)//控制点的数量,也就是线的长度。
          this.blnCanDraw = false;
        
        blnSet = true;
      else
        var count = Utils.DisP(this.lastMousePostion, pos);
        if (count > 1)
          var c = Math.ceil(count);
          for(var i=0; i < c; i++)
            if (i == c - 1)
              this.AddSinglePoint(pos, this.nowR);
            else
              var p = Utils.LerpP(this.lastMousePostion, pos, (i + 1) / c);
              this.AddSinglePoint(p, this.nowR);
            
          
          blnSet = true;
        else 
          // this.AddSinglePoint(pos, this.nowR);
          blnSet = false;
        
      
    
    if (kind == cv.touch_ended)
      // this.CompareBihua();
      // this.ResetDrawPoint();
    
    if (blnSet)
      this.lastMousePostion = this.mousePosition;
    
    this.drawTouch && this.drawTouch.setPoints(this.showPoints);
  
  BSpline2Smooth1(list, blnSet)//曲线算法处理函数
    var aList = [];
    aList = aList.concat(list);
    if (blnSet)
      aList.unshift(list[0]);
      aList.push(list[list.length - 1]);
    
    var tList = [];
    var loc1 = 1;
    var start = , end = ;
    while(loc1 < aList.length - 1)
      start = aList[loc1 - 1];
      end = aList[loc1 + 1];
      tList.push(Utils.LerpP(aList[loc1-1], aList[loc1], 0.5));//添加两点的中点
      this.BSpline2Smooth2(tList, start, aList[loc1], end);//最主要的是这里
      tList.push(Utils.LerpP(aList[loc1], aList[loc1+1], 0.5));
      ++loc1;
    
    var rl = Utils.ResampleByLen(tList, 2);//得到处理之后的点之后,对点数组进行标准化处理,就算输出总长度,每个2个单位距离去一个插值点,得到新数据
    if (rl != null)
      return rl
    else
      return tList;
    
  
  BSpline2Smooth2(list, arg1, arg2, arg3)
    var locx = [];
    var locy = [];
    locx.push((arg1.x + arg2.x) * 0.5);
    locx.push(arg2.x - arg1.x);
    locx.push((arg1.x - 2*arg2.x + arg3.x) * 0.5);
    locy.push((arg1.y + arg2.y) * 0.5);
    locy.push(arg2.y - arg1.y);
    locy.push((arg1.y - 2*arg2.y + arg3.y) * 0.5);
    var loc6 = parseInt(Utils.CountDistance(arg1, arg3));
    var loc7 = 0;
    var loc8 = 0;
    while(loc7 < loc6)
      loc8 = loc7 / loc6;
      var loc5 = 
        x: locx[0] + loc8 * (locx[1] + locx[2] * loc8),
        y: locy[0] + loc8 * (locy[1] + locy[2] * loc8)
      ;
      list.push(loc5);
      loc7++;
    
  

Utils类《-点这里查看,我这里贴出来

以上是关于React-Native画线平滑处理的主要内容,如果未能解决你的问题,请参考以下文章

react-native 怎么画线

Bresenham画线算法

如何让线条边缘平滑?

origin三维图数据过少,曲面不圆滑,如何处理可以使曲面圆滑。拟合、差值等,不能改变原来的走势

基于unity物体定点移动与模拟刹车的细节 GIF 图文详解——线性差值函数以及平滑阻尼的运用和实践(Lerp AND SmoothDamp)

用于手指触摸绘图的 UIBezierPath 平滑曲线