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类《-点这里查看,我这里贴出来

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

苏小败在路上

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值