Unity electronic signature Bezier curve save picture

Reminder: The source code is attached to the post~ Everyone can learn from each other


foreword

Usually when you go to the bank to handle business, you will always encounter electronic signatures, and you can prove your identity by electronically signing documents online. In the recently produced system, the electronic signature function is also required for personnel to review and sign documents online! So organize components for easy access every time! Let's learn together!


1. Component structure

  1. Prefabricated structure
    insert image description here

  2. project structure
    insert image description here

2. Function realization

1. Bezier curve signature

  • Add signImage in the signature area panel, get the mouse and place it on the panel, and then refresh and display the signature content in real time.
  	/// <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);
            }
        }
    }
  • The core drawing method handles drawing curves by obtaining mouse movement and lifting.
   /// <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. Clear

When there is a writing error, the generated signature on the page needs to be cleared and redrawn.

   /// <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. Save

Save it in the specified directory in the form of pictures; or save it in the database in binary form.

 /// <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: In the current code, the image is stored in png format in the Application.streamingAssetsPath + "/QianMing/" path under the project.

4. Replay

Realize replaying signatures in writing order.

 /// <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;

    }


3. Script description

The functional method is classified in SignPanel.cs, bind the script to the Camera, and introduce related content such as brush material.
insert image description here

Summarize

The component structure is simple, the hierarchy is clear, and it is easy to understand. The ui and size can be changed for different occasions. Personal summary and induction, easy to use. Avoid reinventing the wheel~~~

CSDN component download: https://download.csdn.net/download/u014641682/87577410

Guess you like

Origin blog.csdn.net/u014641682/article/details/129538212