Skip to content

Commit 27b3c5e

Browse files
committed
feat(email encrypt): implement CanvasEmail component for obfuscation of display email text
- Add new CanvasEmail component that renders email on canvas - Replace plain email text with CanvasEmail in Footer
1 parent e9f31ba commit 27b3c5e

File tree

2 files changed

+97
-1
lines changed

2 files changed

+97
-1
lines changed

components/CanvasEmail.js

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import { useEffect, useRef, useState } from 'react'
2+
3+
const CanvasEmail = ({ email, className = '' }) => {
4+
const canvasRef = useRef(null)
5+
const textRef = useRef(null)
6+
const [isCopied, setIsCopied] = useState(false)
7+
8+
useEffect(() => {
9+
if (!textRef.current || !canvasRef.current) return
10+
11+
const canvas = canvasRef.current
12+
const ctx = canvas.getContext('2d')
13+
const textElement = textRef.current
14+
15+
// Get computed styles from the hidden text element
16+
const style = window.getComputedStyle(textElement)
17+
const font = style.font
18+
const color = style.color
19+
20+
// Set canvas font and measure text
21+
ctx.font = font
22+
const metrics = ctx.measureText(email)
23+
const fontSize = parseInt(style.fontSize)
24+
const lineHeight = fontSize * 1.2
25+
26+
// Set canvas dimensions
27+
const scale = window.devicePixelRatio || 1
28+
canvas.width = metrics.width * scale
29+
canvas.height = lineHeight * scale
30+
canvas.style.width = `${metrics.width}px`
31+
canvas.style.height = `${lineHeight}px`
32+
33+
// Redraw with high DPI support
34+
ctx.scale(scale, scale)
35+
ctx.font = font
36+
ctx.fillStyle = color
37+
ctx.textBaseline = 'top' // Changed to 'top' for better vertical alignment
38+
ctx.fillText(email, 0, 0)
39+
40+
// Handle copy to clipboard
41+
const handleCopy = e => {
42+
e.preventDefault()
43+
navigator.clipboard.writeText(email).then(() => {
44+
setIsCopied(true)
45+
setTimeout(() => setIsCopied(false), 2000)
46+
})
47+
}
48+
49+
canvas.style.cursor = 'pointer'
50+
canvas.addEventListener('click', handleCopy)
51+
return () => canvas.removeEventListener('click', handleCopy)
52+
}, [email])
53+
54+
return (
55+
<span
56+
className={`relative inline-block align-middle ${className}`}
57+
style={{ lineHeight: 'normal' }}>
58+
{/* Hidden span for measuring text metrics */}
59+
<span
60+
ref={textRef}
61+
style={{
62+
position: 'absolute',
63+
visibility: 'hidden',
64+
whiteSpace: 'nowrap',
65+
font: 'inherit',
66+
pointerEvents: 'none',
67+
userSelect: 'none',
68+
lineHeight: 'normal'
69+
}}></span>
70+
71+
{/* Canvas that displays the text */}
72+
<canvas
73+
ref={canvasRef}
74+
className='inline-block align-middle'
75+
style={{
76+
verticalAlign: 'middle',
77+
backgroundColor: 'transparent',
78+
pointerEvents: 'auto',
79+
font: 'inherit',
80+
lineHeight: 'normal',
81+
display: 'inline-block',
82+
userSelect: 'none',
83+
WebkitUserSelect: 'none',
84+
msUserSelect: 'none',
85+
MozUserSelect: 'none',
86+
KhtmlUserSelect: 'none'
87+
}}
88+
title={isCopied ? 'Copied!' : 'Click to copy'}
89+
aria-label={`Email: ${email}`}
90+
/>
91+
</span>
92+
)
93+
}
94+
95+
export default CanvasEmail

themes/commerce/components/Footer.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import Link from 'next/link'
66
import CONFIG from '../config'
77
import { decryptEmail, handleEmailClick } from '@/lib/plugins/mailEncrypt'
88
import { useRef } from 'react'
9+
import CanvasEmail from '@/components/CanvasEmail'
910

1011
/**
1112
* 页脚
@@ -143,7 +144,7 @@ const Footer = props => {
143144
className='cursor-pointer'
144145
ref={emailIcon}>
145146
<i className='transform hover:scale-125 duration-150 fas fa-envelope dark:hover:text-red-400 hover:text-red-600' />{' '}
146-
{decryptEmail(CONTACT_EMAIL)}
147+
<CanvasEmail email={decryptEmail(CONTACT_EMAIL)} />
147148
</a>
148149
)}
149150
</div>

0 commit comments

Comments
 (0)