您的位置:首页 > Web前端 > React

React-Native画线平滑处理

2016-10-12 10:59 253 查看
参考: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类《-点这里查看,我这里贴出来
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息