提示:源码附在文后~大家互相学习
文章目录
- 前言
- 一、组件结构
- 二、功能实现
- 1.贝塞尔曲线签名
- 2.清除
- 3.保存
- 4.重放
- 三、脚本说明
- 总结
前言
平常去银行办理业务,总会遇到电子签名,通过线上电子签名文件,证明自己身份。近期制作的系统中也需要电子签名功能用于人员线上审核文件签字!所以整理出组件,方便每次使用!大家共同学习!
一、组件结构
- 预制体结构
- 项目结构
二、功能实现
1.贝塞尔曲线签名
- 增加签字区域面板signImage,获取鼠标放置在面板上,再实时刷新显示签字内容。
/// <summary>
/// 获取鼠标当前所在UI
/// </summary>
/// <param name="canvas"></param>
/// <returns></returns>
public GameObject GetOverUI(GameObject canvas)
{
PointerEventData pointerEventData = new PointerEventData(EventSystem.current);
pointerEventData.position = Input.mousePosition;
GraphicRaycaster gr = canvas.GetComponent<GraphicRaycaster>();
List<RaycastResult> results = new List<RaycastResult>();
gr.Raycast(pointerEventData, results);
if (results.Count != 0)
{
return results[0].gameObject;
}
return null;
}
void Update()
{
if (Input.GetMouseButton(0) && GetOverUI(GameObject.Find("Canvas")) == signImage.gameObject)
{
IsUpdataRawImage = true;
OnMouseMove(new Vector3(Input.mousePosition.x, Input.mousePosition.y, 0));
queue.Add(new Vector3(Input.mousePosition.x, Input.mousePosition.y, 0));
BiHua.Add(1);
}
if (Input.GetMouseButtonUp(0) && GetOverUI(GameObject.Find("Canvas")) == signImage.gameObject)
{
OnMouseUp();
queue.Add(new Vector3(Input.mousePosition.x, Input.mousePosition.y, 0));
BiHua.Add(0);
//添加比划
BiHuanIndex++;
BiHuaList.Add(getTexture2d(texRender));
print("BiHuanIndex" + BiHuanIndex + "BiHuaList" + BiHuaList.Count);
//一直刷新显示
if (IsUpdataRawImage == true)
{
DrawImage();
}
if (Input.GetKeyDown(KeyCode.Escape))
{
Application.Quit();
}
if (IsRePlay == true && numA > 0)
{
numA--;
print("现在的Index" + ((queue.Count - 1) - numA).ToString());
if (BiHuaIndex > queue.Count - 2)
{
return;
}
else
{
AutoPlay((Vector3)queue[(queue.Count - 1) - numA], (queue.Count - 1) - numA);
}
}
}
- 核心绘制方法,通过获取鼠标移动、抬起,处理绘制曲线。
/// <summary>
/// 当鼠标移动时
/// </summary>
/// <param name="pos"></param>
void OnMouseMove(Vector3 pos)
{
if (startPosition == Vector3.zero)
{
startPosition = new Vector3(Input.mousePosition.x, Input.mousePosition.y, 0);
}
endPosition = pos;
float distance = Vector3.Distance(startPosition, endPosition);
brushScale = SetScale(distance);
ThreeOrderBézierCurse(pos, distance, 4.5f);
startPosition = endPosition;
lastDistance = distance;
}
void OnMouseUp()
{
startPosition = Vector3.zero;
//brushScale = 0.5f;
a = 0;
b = 0;
s = 0;
}
// <summary>
/// 三阶贝塞尔曲线,获取连续4个点坐标,通过调整中间2点坐标,画出部分(我使用了num/1.5实现画出部分曲线)来使曲线平滑;通过速度控制曲线宽度。
/// </summary>
/// <param name="pos"></param>
/// <param name="distance"></param>
/// <param name="targetPosOffset"></param>
private void ThreeOrderBézierCurse(Vector3 pos, float distance, float targetPosOffset)
{
//记录坐标
PositionArray1[b] = pos;
b++;
//记录速度
speedArray[s] = distance;
s++;
if (b == 4)
{
Vector3 temp1 = PositionArray1[1];
Vector3 temp2 = PositionArray1[2];
//修改中间两点坐标
Vector3 middle = (PositionArray1[0] + PositionArray1[2]) / 2;
PositionArray1[1] = (PositionArray1[1] - middle) * 1.5f + middle;
middle = (temp1 + PositionArray1[3]) / 2;
PositionArray1[2] = (PositionArray1[2] - middle) * 2.1f + middle;
for (int index1 = 0; index1 < num / 1.5f; index1++)
{
float t1 = (1.0f / num) * index1;
Vector3 target = Mathf.Pow(1 - t1, 3) * PositionArray1[0] +
3 * PositionArray1[1] * t1 * Mathf.Pow(1 - t1, 2) +
3 * PositionArray1[2] * t1 * t1 * (1 - t1) + PositionArray1[3] * Mathf.Pow(t1, 3);
//float deltaspeed = (float)(distance - lastDistance) / num;
//获取速度差值(存在问题,参考)
float deltaspeed = (float)(speedArray[3] - speedArray[0]) / num;
//float randomOffset = Random.Range(-1/(speedArray[0] + (deltaspeed * index1)), 1 / (speedArray[0] + (deltaspeed * index1)));
//模拟毛刺效果
float randomOffset = Random.Range(-targetPosOffset, targetPosOffset);
DrawBrush(texRender, (int)(target.x + randomOffset), (int)(target.y + randomOffset), brushTypeTexture, brushColor, SetScale(speedArray[0] + (deltaspeed * index1)));
}
PositionArray1[0] = temp1;
PositionArray1[1] = temp2;
PositionArray1[2] = PositionArray1[3];
speedArray[0] = speedArray[1];
speedArray[1] = speedArray[2];
speedArray[2] = speedArray[3];
b = 3;
s = 3;
}
else
{
DrawBrush(texRender, (int)endPosition.x, (int)endPosition.y, brushTypeTexture,
brushColor, brushScale);
}
}
2.清除
书写错误时,需要清除页面上已生成的签字,重新绘制。
/// <summary>
/// 重写
/// </summary>
public void OnClickClear()
{
text.gameObject.SetActive(false);
Clear(texRender);
BiHuaList.Clear();
BiHuanIndex = 0;
Resources.UnloadUnusedAssets();
GC.Collect();
BiHuaIndex = 0;
queue.Clear();
BiHua.Clear();
IsUpdataRawImage = true;
startPosition = Vector3.zero;
//brushScale = 0.5f;
a = 0;
b = 0;
s = 0;
isClear = true;
}
3.保存
以图片形式保存到指定目录中;或以二进制形式保存到数据库。
/// <summary>
/// 将书写好的效果保存成为Png
/// </summary>
public void Save()
{
string filepath = System.DateTime.Now.ToString("yyMMddHHmmss") + ".png";
StartCoroutine(SaveRenderTextureToPNG(texRender, filePath, filepath));
}
/// <summary>
/// 将RenderTexture保存成一张png图片
/// </summary>
/// <param name="rt"></param>
/// <param name="contents"></param>
/// <param name="pngName"></param>
/// <returns></returns>
IEnumerator SaveRenderTextureToPNG(RenderTexture rt, string contents, string pngName)
{
RenderTexture prev = RenderTexture.active;
RenderTexture.active = rt;
Texture2D png = new Texture2D(rt.width, rt.height, TextureFormat.ARGB32, false);
png.ReadPixels(new Rect(0, 0, rt.width, rt.height), 0, 0);
Color[] cc = png.GetPixels();
for (int i = 0; i < cc.Length; i++)
if (cc[i].r == 1)
cc[i] = Color.clear;
png.SetPixels(cc);
byte[] bytes = png.EncodeToPNG();
//-------------1.可舍弃方式2,在此处上传图片至服务器存储------------
//signPicture.sprite = ByteToSprite(bytes, 15, 15);//反显数据库取回图片
//-------------2.此处为保存至本地-----------
if (!Directory.Exists(contents))
Directory.CreateDirectory(contents);
FileStream file = File.Open(contents + "/" + pngName, FileMode.Create);
BinaryWriter writer = new BinaryWriter(file);
writer.Write(bytes);
file.Close();
Texture2D.DestroyImmediate(png);
RenderTexture.active = prev;
text.text = "已保存成功";
text.gameObject.SetActive(true);
yield return writer;
}
PS:目前代码中将图片以png格式存放至项目下 Application.streamingAssetsPath + "/QianMing/"路径下。
4.重放
实现按书写顺序,重播签名。
/// <summary>
/// 重放功能
/// </summary>
public void RePlay()
{
text.gameObject.SetActive(false);
BiHuaIndex = 0;
Clear(texRender);
Proxy = queue[0];
IsRePlay = true;
numA = queue.Count;
BiHuanIndex = 0;
BiHuaList.Clear();
startPosition = Vector3.zero;
a = 0;
b = 0;
s = 0;
}
三、脚本说明
功能方法归类在SignPanel.cs中,将脚本绑定在Camera上,引入笔刷材质等相关内容。
总结
组件结构简单,层级明朗,便于理解。可更改ui和尺寸用于不同场合。个人总结归纳,便于使用。避免重复造轮子~~~