HTML5 の canvas で電子サインのお試し

#1.HTML5 の canvasについて
Canvasとは、HTML5から新しく追加される図形を描くための技術仕様で、 HTMLの<canvas>要素とJavaScriptを組み合わせて図形を描画します。 JavaScript以外のスクリプトでも描画可能ですが、ほとんどの場合JavaScriptが標準的に利用されています。
　　[HTML5 Canvas](https://www.w3schools.com/html/html5_canvas.asp)
　　[HTML5 canvas 参考手册](http://www.runoob.com/tags/ref-canvas.html)

#2.電子サインのお試し
**Visualforce + Apex で電子サイン機能を実装します。
サインの結果は「テキストエリア (リッチ)」項目または添付ファイルとして保存します。**

**【実装内容】**

- 電子サインオブジェクト
- Apex Controller
- Visualforce

##2.1.電子サインオブジェクト
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332907/2e954a78-a963-c0e5-e0f1-2fcad66255cc.png)

##2.2.Apex Controller
```java:ElectronicSignatureController
global class ElectronicSignatureController {
    @RemoteAction
    global static ReturnValue saveSignature(String signValue) {
        ReturnValue returnValue = new ReturnValue();
        
        try{
            ElectronicSignature__c eSignature = new ElectronicSignature__c();
            
            // テキストエリア (リッチ)
            eSignature.SignDateTime__c = System.now();
            eSignature.SignValue__c = '<img src="' + signValue + '"/>';
            insert eSignature;
        
            // 添付ファイル
            Attachment signAttachment = new Attachment();
            signAttachment.ParentID = eSignature.Id;
            signAttachment.Body = EncodingUtil.base64Decode(signValue.replace('data:image/png;base64,', ''));
            signAttachment.contentType = 'image/png';
            signAttachment.Name = '電子サイン';
            signAttachment.OwnerId = UserInfo.getUserId();
            insert signAttachment;

            // 正常終了
            returnValue.isSuccess = true;
            returnValue.message = '処理が正常に行いました。電子サインレコード詳細画面に遷移します。';
            returnValue.recordId = eSignature.Id;
        } catch(Exception e){
            // 異常終了
            returnValue.isSuccess = false;
            returnValue.message = 'システムエラーが発生しました。' + e.getMessage();
        }
        return returnValue;
    }
    
    global class ReturnValue {
        global Boolean isSuccess { get; set; }
        global String message { get; set; }
        global String recordId {get; set;}
    }
}
```

##2.2.Visualforce
```javascript:ElectronicSignaturePage
<apex:page docType="html-5.0" controller="ElectronicSignatureController" showheader="false" sidebar="false" standardStylesheets="false">
    <style>
        .container {
            text-align: center;
            font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
            color: cadetblue;
            font-weight: 500;
            font-size: 14px;
        }

        .button {
            font-family: calibri;
            border-radius: 8px;
            background-color: rgb(51, 116, 116);
            height: 36px;
            color: azure;
            font-size: 17px;
            border-width: 0px;
            width: 116px;
        }
    </style>
    <apex:form id="theForm">
        <div class="container">
            <h1 class="labelCol vfLabelColTextWrap ">電子サイン</h1>
            <canvas id="signatureCanvas" height="100px" width="350px" style="border: 3px solid antiquewhite; border-radius: 8px;" ></canvas>
        </div>
        <br/>
        <div style="margin-left: 38%;">
            <apex:commandButton value="保存" onclick="saveSignature();return false;" styleClass="button"/>&nbsp;&nbsp;&nbsp;
            <apex:commandButton value="クリア" onclick="clearSign();return false;" styleClass="button"/>
        </div>
    </apex:form>

    <script>
        var canvas;
        var context;
        var isDrawing = false;
        var prevX, prevY, currX, currY = 0;

        function start(event) {
            event.preventDefault();

            isDrawing = true;
            prevX = currX;
            prevX = currY;
            currX = event.clientX - canvas.offsetLeft;
            currY = event.clientY - canvas.offsetTop;

            context.beginPath();
            context.fillStyle = "cadetblue";
            context.fillRect(currX, currY, 2, 2);
            context.closePath();
        }

        function draw(event) {
            event.preventDefault();
            if (isDrawing) {
                prevX = currX;
                prevY = currY;
                currX = event.clientX - canvas.offsetLeft;
                currY = event.clientY - canvas.offsetTop;
                context.beginPath();
                context.moveTo(prevX, prevY);
                context.lineTo(currX, currY);
                context.strokeStyle = "cadetblue";
                context.lineWidth = "2";
                context.stroke();
                context.closePath();
            }
        }

        function stop(event) {
            if (isDrawing) {
                context.stroke();
                context.closePath();
                isDrawing = false;
            }
        }

        function clearSign() {
            context.clearRect(0, 0, canvas.width, canvas.height);
        }

        canvas = document.getElementById("signatureCanvas");
        canvas.addEventListener("mousedown", start, false);
        canvas.addEventListener("mousemove", draw, false);
        canvas.addEventListener("mouseup", stop, false);
        canvas.addEventListener("mouseout", stop, false);
        canvas.addEventListener("touchstart", start, false);
        canvas.addEventListener("touchmove", draw, false);
        canvas.addEventListener("touchend", stop, false);
        
        context = canvas.getContext("2d");
        context.strokeStyle = "black";
        context.lineWidth = "2";
        
        isDrawing = false;

        function saveSignature() {
            var strDataURI = canvas.toDataURL();
            var returnValue = ElectronicSignatureController .saveSignature(strDataURI, callback);
        }

        function callback(returnValue) {
            if (returnValue.message != null && returnValue.message != "") {
                alert(returnValue.message);
            }
            
            if (returnValue.isSuccess) {
                this.parent.location.href= '/'+returnValue.recordId;
            }
        }
    </script>
</apex:page>
```
#3.結果確認
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332907/2d8b5b58-92c7-6e2e-0373-ece4143c8c6e.png)

#4.参照
[Capture Signature on iPad Using Visualforce Page](http://teach-force.blogspot.com/2014/02/capture-signature-on-ipad-using.html)
[HTML5のcanvasに手書きやマウスのドラッグで絵を書く](https://mam-mam.net/javascript/draw_js.html)