自定义签名工具相信大家都轻车熟路,通过监听屏幕onTouchEvent事件,分别在按下(ACTION_DOWN)、抬起(ACTION_UP)、移动(ACTION_MOVE) 动作中处理触碰点的收集和绘制,很容易就达到了手签笔的效果。其中无非又涉及到线条的撤回、取消、清除、画笔的粗细,也就是对收集的点集合和线集合的增删操作以及画笔颜色宽的的更改。这些功能都在 实现一个自定义有限制区域的图例(角度自识别)涂鸦工具类(上) https://juejin.cn/post/7202260878930591802中介绍过。
作者:似曾相识2022 链接:https://juejin.cn/post/7244192848063627325
但就在前不久遇到一个需求是要求手签笔能够和咱们使用钢笔签名类似的效果,当然这个功能目前是有一些公司有成熟的SDK的,但我们的需求是要不借助SDK,自己实现笔锋效果。那么,如何使画笔带笔锋呢?废话不多说,先上效果图:
要实现笔锋效果我们需要考虑几个因素:笔速、笔宽、按压力度(针对手写笔)。因为在onTouchEvent回调的次数是不变的,一旦笔速变快两点之间距离就被拉长。此时的笔宽不能保持在上一笔的宽度,需要我们通过计算插入新的点,同时计算出对应点的宽度。同理当我们笔速慢的时候,需要通过计算删除信息相近的点。要想笔锋自然,当然贝塞尔曲线是必不可少的。
这里我们暂时没有将笔的按压值作为笔宽的计算,仅仅通过笔速来计算笔宽。
/**
* 计算新的宽度信息
*/
public double calcNewWidth(double curVel, double lastVel,double factor) {
double calVel = curVel * 0.6 + lastVel * (1 - 0.6);
double vfac = Math.log(factor * 2.0f) * (-calVel);
double calWidth = mBaseWidth * Math.exp(vfac);
return calWidth;
}
/**
* 获取点信息
*/
public ControllerPoint getPoint(double t) {
float x = (float) getX(t);
float y = (float) getY(t);
float w = (float) getW(t);
ControllerPoint point = new ControllerPoint();
point.set(x, y, w);
return point;
}
/**
* 三阶曲线的控制点
*/
private double getValue(double p0, double p1, double p2, double t) {
double a = p2 - 2 * p1 + p0;
double b = 2 * (p1 - p0);
double c = p0;
return a * t * t + b * t + c;
}
最后也是最关键的地方,不再使用drawLine方式画线,而是通过drawOval方式画椭圆。通过前后两点计算出椭圆的四个点,通过笔宽计算出绘制椭圆的个数并加入椭圆集。最后在onDraw方法中绘制。
/**
* 两点之间将视图收集的点转为椭圆矩阵 实现笔锋效果
*/
public static ArrayList twoPointsTransRectF(double x0, double y0, double w0, double x1, double y1, double w1, float paintWidth, int color) {
ArrayList list = new ArrayList<>();
//求两个数字的平方根 x的平方+y的平方在开方记得X的平方+y的平方=1,这就是一个园
double curDis = Math.hypot(x0 - x1, y0 - y1);
int steps;
//绘制的笔的宽度是多少,绘制多少个椭圆
if (paintWidth < 6) {
steps = 1 + (int) (curDis / 2);
} else if (paintWidth > 60) {
steps = 1 + (int) (curDis / 4);
} else {
steps = 1 + (int) (curDis / 3);
}
double deltaX = (x1 - x0) / steps;
double deltaY = (y1 - y0) / steps;
double deltaW = (w1 - w0) / steps;
double x = x0;
double y = y0;
double w = w0;
for (int i = 0; i < steps; i++) {
RectF oval = new RectF();
float top = (float) (y - w / 2.0f);
float left = (float) (x - w / 4.0f);
float right = (float) (x + w / 4.0f);
float bottom = (float) (y + w / 2.0f);
oval.set(left, top, right, bottom);
//收集椭圆矩阵信息
list.add(new SvgPointBean(oval, color));
x += deltaX;
y += deltaY;
w += deltaW;
}
return list;
}
至此一个简单的带笔锋的手写签名就实现了。 最后附上参考链接Github(https://github.com/GdinKing/HandWrite)
关注我获取更多知识或者投稿