最近项目中有个需要签名的地方,要用到手写签名,开始只是简单的实现手写签名,如图:

后来领导说,能不能实现像毛笔那样签名的效果,那好吧,领导说怎样就怎样吧,而且我也觉得这里用毛笔效果会更好些。那就只好运用贝塞尔曲线的原理了。实现如下:

/*** This view implements the drawing canvas.* * It handles all of the input events and drawing functions.*/class PaintView extends View {private Paint paint;private Canvas cacheCanvas;private Bitmap cachebBitmap;private Path path;private List<TimePoint> mPoints = new ArrayList<>();private float mVelocityFilterWeight;private float mLastTouchX;private float mLastTouchY;private float mLastVelocity;private float mLastWidth;private int mMinWidth;private int mMaxWidth;private RectF mDirtyRect;public Bitmap getCachebBitmap() {return cachebBitmap;}public void setSignatureBitmap(Bitmap signature) {clear();ensureSignatureBitmap();RectF tempSrc = new RectF();RectF tempDst = new RectF();int dWidth = signature.getWidth();int dHeight = signature.getHeight();int vWidth = getWidth();int vHeight = getHeight();// Generate the required transform.tempSrc.set(0, 0, dWidth, dHeight);tempDst.set(0, 0, vWidth, vHeight);Matrix drawMatrix = new Matrix();drawMatrix.setRectToRect(tempSrc, tempDst, Matrix.ScaleToFit.START);Canvas canvas = new Canvas(cachebBitmap);canvas.drawBitmap(signature, drawMatrix, null);// setIsEmpty(false);invalidate();}public PaintView(Context context, AttributeSet attrs) {super(context, attrs);TypedArray a = context.getTheme().obtainStyledAttributes(attrs,R.styleable.SignaturePad, 0, 0);// Configurable parameterstry {mMinWidth = a.getDimensionPixelSize(R.styleable.SignaturePad_minWidth, convertDpToPx(3));mMaxWidth = a.getDimensionPixelSize(R.styleable.SignaturePad_maxWidth, convertDpToPx(12));mVelocityFilterWeight = a.getFloat(R.styleable.SignaturePad_velocityFilterWeight, 0.6f);} finally {a.recycle();}init();}private void init() {paint = new Paint();paint.setAntiAlias(true);paint.setStrokeJoin(Paint.Join.ROUND);paint.setStyle(Paint.Style.STROKE);paint.setColor(Color.BLACK);path = new Path();cachebBitmap = Bitmap.createBitmap(p.width, (int) (p.height),// *// 0.8),Config.ARGB_8888);cacheCanvas = new Canvas(cachebBitmap);cacheCanvas.drawColor(Color.WHITE);mDirtyRect = new RectF();mLastVelocity = 0;mLastWidth = (mMinWidth + mMaxWidth) / 2;path.reset();invalidate();}/*** Set the velocity filter weight.* * @param velocityFilterWeight*            the weight.*/public void setVelocityFilterWeight(float velocityFilterWeight) {mVelocityFilterWeight = velocityFilterWeight;}private void ensureSignatureBitmap() {if (cachebBitmap == null) {cachebBitmap = Bitmap.createBitmap(p.width, (int) (p.height),// *// 0.8),Config.ARGB_8888);cacheCanvas = new Canvas(cachebBitmap);}}public void clear() {mPoints.clear();mLastVelocity = 0;mLastWidth = (mMinWidth + mMaxWidth) / 2;path.reset();if (cacheCanvas != null) {paint.setColor(BACKGROUND_COLOR);cacheCanvas.drawPaint(paint);paint.setColor(Color.BLACK);cacheCanvas.drawColor(Color.WHITE);invalidate();}}@Overrideprotected void onDraw(Canvas canvas) {// canvas.drawColor(BRUSH_COLOR);if (cachebBitmap != null) {if (topBitmap == null) {topBitmap = Bitmap.createBitmap(top_layout.getMeasuredWidth(),top_layout.getMeasuredHeight(), Config.ARGB_8888);top_layout.draw(new Canvas(topBitmap));setSignatureBitmap(topBitmap);}canvas.drawBitmap(cachebBitmap, 0, 0, paint);// canvas.drawPath(path, paint);}}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {int curW = cachebBitmap != null ? cachebBitmap.getWidth() : 0;int curH = cachebBitmap != null ? cachebBitmap.getHeight() : 0;if (curW >= w && curH >= h) {return;}if (curW < w)curW = w;if (curH < h)curH = h;Bitmap newBitmap = Bitmap.createBitmap(curW, curH,Bitmap.Config.ARGB_8888);Canvas newCanvas = new Canvas();newCanvas.setBitmap(newBitmap);if (cachebBitmap != null) {newCanvas.drawBitmap(cachebBitmap, 0, top_layout.getHeight(),null);}cachebBitmap = newBitmap;cacheCanvas = newCanvas;}@Overridepublic boolean onTouchEvent(MotionEvent event) {if (!isEnabled())return false;float x = event.getX();float y = event.getY();switch (event.getAction()) {case MotionEvent.ACTION_DOWN: {getParent().requestDisallowInterceptTouchEvent(true);mPoints.clear();mLastTouchX = x;mLastTouchY = y;path.moveTo(x, y);addPoint(new TimePoint(x, y));}case MotionEvent.ACTION_MOVE: {// path.quadTo(cur_x, cur_y, x, y);resetDirtyRect(x, y);addPoint(new TimePoint(x, y));break;}case MotionEvent.ACTION_UP: {// cacheCanvas.drawPath(path, paint);// path.reset();// cur_x = x;// cur_y = y;resetDirtyRect(x, y);addPoint(new TimePoint(x, y));getParent().requestDisallowInterceptTouchEvent(true);break;}default:return false;}// invalidate();invalidate((int) (mDirtyRect.left - mMaxWidth),(int) (mDirtyRect.top - mMaxWidth),(int) (mDirtyRect.right + mMaxWidth),(int) (mDirtyRect.bottom + mMaxWidth));return true;}public void addPoint(TimePoint point) {mPoints.add(point);if (mPoints.size() > 2) {if (mPoints.size() == 3)mPoints.add(0, mPoints.get(0));ControlTimedPoints tmp = calculateCurveControlPoints(mPoints.get(0), mPoints.get(1), mPoints.get(2));TimePoint c2 = tmp.c2;tmp = calculateCurveControlPoints(mPoints.get(1),mPoints.get(2), mPoints.get(3));TimePoint c3 = tmp.c1;Bezier curve = new Bezier(mPoints.get(1), c2, c3,mPoints.get(2));TimePoint startPoint = curve.startPoint;TimePoint endPoint = curve.endPoint;float velocity = endPoint.getSpeed(startPoint);velocity = Float.isNaN(velocity) ? 0.0f : velocity;velocity = mVelocityFilterWeight * velocity+ (1 - mVelocityFilterWeight) * mLastVelocity;// The new width is a function of the velocity. Higher// velocities// correspond to thinner strokes.float newWidth = strokeWidth(velocity);// The Bezier's width starts out as last curve's final width,// and// gradually changes to the stroke width just calculated. The// new// width calculation is based on the velocity between the// Bezier's// start and end mPoints.addBezier(curve, mLastWidth, newWidth);mLastVelocity = velocity;mLastWidth = newWidth;// Remove the first element from the list,// so that we always have no more than 4 mPoints in mPoints// array.mPoints.remove(0);}}/*** Resets the dirty region when the motion event occurs.* * @param eventX*            the event x coordinate.* @param eventY*            the event y coordinate.*/private void resetDirtyRect(float eventX, float eventY) {// The mLastTouchX and mLastTouchY were set when the ACTION_DOWN// motion event occurred.mDirtyRect.left = Math.min(mLastTouchX, eventX);mDirtyRect.right = Math.max(mLastTouchX, eventX);mDirtyRect.top = Math.min(mLastTouchY, eventY);mDirtyRect.bottom = Math.max(mLastTouchY, eventY);}/*** * @param curve* @param startWidth* @param endWidth*/private void addBezier(Bezier curve, float startWidth, float endWidth) {ensureSignatureBitmap();float originalWidth = paint.getStrokeWidth();float widthDelta = endWidth - startWidth;float drawSteps = (float) Math.floor(curve.length());for (int i = 0; i < drawSteps; i++) {// Calculate the Bezier (x, y) coordinate for this step.float t = ((float) i) / drawSteps;float tt = t * t;float ttt = tt * t;float u = 1 - t;float uu = u * u;float uuu = uu * u;float x = uuu * curve.startPoint.x;x += 3 * uu * t * curve.control1.x;x += 3 * u * tt * curve.control2.x;x += ttt * curve.endPoint.x;float y = uuu * curve.startPoint.y;y += 3 * uu * t * curve.control1.y;y += 3 * u * tt * curve.control2.y;y += ttt * curve.endPoint.y;// 设置画笔的大小paint.setStrokeWidth(startWidth + ttt * widthDelta);//控制线显示的范围if (y > top_layout.getHeight()) {cacheCanvas.drawPoint(x, y, paint);}expandDirtyRect(x, y);}paint.setStrokeWidth(originalWidth);}private ControlTimedPoints calculateCurveControlPoints(TimePoint s1,TimePoint s2, TimePoint s3) {float dx1 = s1.x - s2.x;float dy1 = s1.y - s2.y;float dx2 = s2.x - s3.x;float dy2 = s2.y - s3.y;TimePoint m1 = new TimePoint((s1.x + s2.x) / 2.0f,(s1.y + s2.y) / 2.0f);TimePoint m2 = new TimePoint((s2.x + s3.x) / 2.0f,(s2.y + s3.y) / 2.0f);float l1 = (float) Math.sqrt(dx1 * dx1 + dy1 * dy1);float l2 = (float) Math.sqrt(dx2 * dx2 + dy2 * dy2);float dxm = (m1.x - m2.x);float dym = (m1.y - m2.y);float k = l2 / (l1 + l2);TimePoint cm = new TimePoint(m2.x + dxm * k, m2.y + dym * k);float tx = s2.x - cm.x;float ty = s2.y - cm.y;return new ControlTimedPoints(new TimePoint(m1.x + tx, m1.y + ty),new TimePoint(m2.x + tx, m2.y + ty));}private float strokeWidth(float velocity) {return Math.max(mMaxWidth / (velocity + 1), mMinWidth);}private int convertDpToPx(float dp) {return Math.round(dp* (getResources().getDisplayMetrics().xdpi / DisplayMetrics.DENSITY_DEFAULT));}public Bitmap convertViewToBitmap(View view) {view.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());view.buildDrawingCache();Bitmap bitmap = view.getDrawingCache();return bitmap;}/*** Called when replaying history to ensure the dirty region includes all* mPoints.* * @param historicalX*            the previous x coordinate.* @param historicalY*            the previous y coordinate.*/private void expandDirtyRect(float historicalX, float historicalY) {if (historicalX < mDirtyRect.left) {mDirtyRect.left = historicalX;} else if (historicalX > mDirtyRect.right) {mDirtyRect.right = historicalX;}if (historicalY < mDirtyRect.top) {mDirtyRect.top = historicalY;} else if (historicalY > mDirtyRect.bottom) {mDirtyRect.bottom = historicalY;}}}

最后呈上效果图:

使用贝塞尔曲线算法实现毛笔签名效果相关推荐

  1. 贝塞尔曲线算法之JS获取点

    什么是贝塞尔曲线? 贝塞尔曲线(Bézier curve),又称贝兹曲线或贝济埃曲线,是应用于二维图形应用程序的数学曲线. 这个一阶贝塞尔曲线绘制过程,黑点按百分比t从P0->P1移动,看不出什 ...

  2. android 飞入购物车,贝塞尔曲线实现物品飞入购物车效果

    前言 美团外卖的添加物品时,物品飞入购物车的效果很炫,网上参考了诸多大神的实现,本文参考了某位大神的实现,具体链接网络,悲催啊.不过再此仍然表示感谢.最后将此实现整合成一个工具类,方便大家使用. 此文 ...

  3. 贝塞尔曲线实现手写签名

    项目前些天要求完成手写签名并生成图片的功能,找了很多的文章,大多数比较麻烦,而且也不需要实现那么多附带的功能.只需要实现简单的签名就可以,如何实现呢?其实这个手写签名和画板是一样的思路. 我们利用 p ...

  4. java 三次贝塞尔曲线算法_转:穿过已知点画平滑曲线(3次贝塞尔曲线)

    Interpolation with Bezier Curves  贝塞尔插值 A very simple method of smoothing polygons 一种非常简单的多边形平滑方法 翻译 ...

  5. 小程序二次贝塞尔曲线,购物车商品曲线飞入效果

    前段时间闲暇的时候看到一个贝塞尔曲线算法的文章,试着在小程序里去实现小程序的贝塞尔曲线算法,及其效果. 主要应用到的技术点: 1.小程序wxss布局,以及数据绑定 2.js二次bezier曲线算法 核 ...

  6. 基于三阶贝塞尔曲线的数据平滑算法

    文章目录 前言 贝塞尔曲线 算法描述 算法实现 参考资料 前言 很多文章在谈及曲线平滑的时候,习惯使用拟合的概念,我认为这是不恰当的.平滑后的曲线,一定经过原始的数据点,而拟合曲线,则不一定要经过原始 ...

  7. 贝塞尔曲线(Bezier)之水波纹的手机充电动画效果(一)

    博主声明: 转载请在开头附加本文链接及作者信息,并标记为转载.本文由博主 威威喵 原创,请多支持与指教. 本文首发于此   博主:威威喵  |  博客主页:https://blog.csdn.net/ ...

  8. android高级UI之贝塞尔曲线<上>---基本概念、德卡斯特里奥算法

    在上一次android高级UI之Canvas综合案例操练 - cexo - 博客园对于Android UI绘制中核心的Canvas进行了相关的学习,这块的学习也中断一年多了,既然主业是Android开 ...

  9. 贝塞尔曲线(Bezier)之花束直播爱心点赞动画效果

    博主声明: 转载请在开头附加本文链接及作者信息,并标记为转载.本文由博主 威威喵 原创,请多支持与指教. 本文首发于此   博主:威威喵  |  博客主页:https://blog.csdn.net/ ...

最新文章

  1. 关于类、方法、对象(实例):静态方法
  2. 【读书笔记】iOS-ARC-不要向已经释放的对象发送消息
  3. 数据库中插入英文数字正常,插入中文错误解决方案
  4. 用C#写的一个注册表操作类
  5. iOS之深入解析dispatch source的原理与功能
  6. DR5白金版 for mac(PS一键磨皮插件Delicious Retouch)支持ps 2022
  7. 7-27 通讯录的录入与显示 (10 分)
  8. php 多态实现案例
  9. .Net转Java自学之路—基础巩固篇二十二(XML)
  10. python胶水语言融合其他语言_1. Python中如何使用其他语言?(python的胶水作用,python又叫胶水语言)...
  11. 编码规范重要性_沟通比您的编码技能更重要
  12. 关于IP网络号和主机号的原理
  13. getTime()获取时间戳方法与时区有关
  14. #Linux#进程间通信# 管道(pipe)-匿名管道pipe
  15. 图形化WiFi密码的破解
  16. 风控为本 开鑫金服让互联网金融更有安全感
  17. URP——着色器和材质——复杂光照Complex Lit
  18. squid服务的应用[转]
  19. 如何把照片背景变成透明?怎么把图片去白底变透明?
  20. Win10 更新又爆 BUG:删除音频驱动;苹果拒绝法院送达禁令裁定

热门文章

  1. 头指针和头结点意义和区别
  2. AnySearch快速搜索文件(android手机版everything)
  3. 【预测模型-GRNN】基于遗传算法优化GRNN实现数据回归拟合matlab源码
  4. 地域为何消失——解读《消失的地域:电子媒介对社会行为的影响》
  5. 抽水蓄能电站孕育的绿色数据中心
  6. 投影仪家里用什么牌子好?哪种投影仪性价比高
  7. angular 结合 nodejs连接数据库在webstorm服务器中启动
  8. checkbox的disabled属性
  9. python keras模块安装检测_Keras安装与测试遇到的坑
  10. RabbitMQ:Shovel模式的搭建和测试