Skip to content

Instantly share code, notes, and snippets.

@stevebauman
Last active December 28, 2023 20:18
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save stevebauman/cf41218606bb753bfb2e2fb788967828 to your computer and use it in GitHub Desktop.
Save stevebauman/cf41218606bb753bfb2e2fb788967828 to your computer and use it in GitHub Desktop.
A VueJS Signature canvas field, using szimek/signature_pad
<template>
<div>
<canvas ref="canvas"></canvas>
<div class="clearfix"></div>
<div class="btn-group">
<button @click="clear" type="button" class="btn btn-default">
<i class="fa fa-times"></i>
Clear Signature
</button>
</div>
</div>
</template>
<style>
.signature {
border: 2px solid #cbc9c6;
border-radius: 5px;
}
</style>
<script>
import SignaturePad from 'signature_pad';
export default {
props: {
value: String,
},
data() {
return {
pad: null,
};
},
mounted() {
this.pad = new SignaturePad(this.$refs.canvas, {
onEnd: () => {
this.$emit('input', this.pad.toDataURL());
}
});
},
methods: {
clear() {
this.pad.clear();
this.$emit('input', this.pad.toDataURL());
},
}
}
</script>
@agm1984
Copy link

agm1984 commented Dec 28, 2023

This code works great except the cursor draws in wack position on my MacOS + HD monitor, so I found this code which fixes it:

        const ratio =  Math.max(window.devicePixelRatio || 1, 1);
        canvas.width = canvas.offsetWidth * ratio;
        canvas.height = canvas.offsetHeight * ratio;
        canvas.getContext("2d").scale(ratio, ratio);
        this.pad.clear(); // otherwise isEmpty() might return incorrect value

You'd add that at the end of your mounted lifecycle.

@stevebauman
Copy link
Author

Thanks for that @agm1984! Do you mean inside of the onEnd function? Or literally at the end of the mounted function? I'll add this into the gist 🙏

@agm1984
Copy link

agm1984 commented Dec 28, 2023

No I mean just after it, and I wasn't able to get the onEnd handler to work, so I just removed it.

Here's my current component (in Vue 3):

<script setup>
import { ref, onMounted } from 'vue';
import SignaturePad from 'signature_pad';
import Button from 'primevue/button';

defineProps({
    modelValue: {
        type: String,
        required: true,
    },
});

const pad = ref(null);

const emit = defineEmits(['update:model-value']);

onMounted(() => {
    let canvas = document.querySelector('canvas');
    pad.value = new SignaturePad(canvas, {
        backgroundColor: 'rgb(255, 255, 255, 0)',
    });

    const ratio =  Math.max(window.devicePixelRatio || 1, 1);
    canvas.width = canvas.offsetWidth * ratio;
    canvas.height = canvas.offsetHeight * ratio;
    canvas.getContext("2d").scale(ratio, ratio);
    pad.value.clear(); // otherwise isEmpty() might return incorrect value
});

const clear = () => {
    pad.value.clear();
    emit('update:model-value', pad.value.toDataURL());
};

const save = () => {
    const data = pad.value.toDataURL();
    emit('update:model-value', data);
};
</script>

<template>
    <div>
        <canvas id="signature-pad" class="signature"></canvas>

        <div class="flex justify-between mt-4">
            <Button
                type="button"
                class="p-button-secondary"
                label="Reset"
                @click="clear"
            />

            <Button
                type="button"
                label="Save"
                @click="save"
            />
        </div>
    </div>
</template>

<style>
    .signature {
        @apply border rounded-md;
    }
</style>

My usage of v-model is a bit strange, but it's still in progress, it should be plenty for someone to work with. My needs are emerging as I work with the designer to handle templates and custom signatures.

@stevebauman
Copy link
Author

Awesome, thanks for sharing @agm1984! 🙏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment