Unity 电子签名 贝塞尔曲线 保存图片

提示:源码附在文后~大家互相学习


前言

平常去银行办理业务,总会遇到电子签名,通过线上电子签名文件,证明自己身份。近期制作的系统中也需要电子签名功能用于人员线上审核文件签字!所以整理出组件,方便每次使用!大家共同学习!


一、组件结构

  1. 预制体结构
    在这里插入图片描述

  2. 项目结构
    在这里插入图片描述

二、功能实现

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和尺寸用于不同场合。个人总结归纳,便于使用。避免重复造轮子~~~

CSDN组件下载:https://download.csdn.net/download/u014641682/87577410

Supongo que te gusta

Origin blog.csdn.net/u014641682/article/details/129538212
Recomendado
Clasificación