在线效果地址
离线画板画图
画板代码
<html>
<head>
<title>You are not connected to the Internet</title>
<meta property="og:url" content="https://dev.to/offline.html">
<meta property="og:title" content="Looks like you've lost your Internet connection">
<meta property="og:image" content="https://thepracticaldev.s3.amazonaws.com/i/aio6nc08tpcqavej512d.png">
<meta property="og:description" content="Maybe you could go outside and get some fresh air.">
<meta property="og:site_name" content="The DEV Community">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:site" content="@ThePracticalDev">
<meta name="twitter:title" content="Looks like you've lost your Internet connection">
<meta name="twitter:description" content="Maybe you could go outside and get some fresh air.">
<meta name="twitter:image:src" content="https://thepracticaldev.s3.amazonaws.com/i/aio6nc08tpcqavej512d.png">
<style>
html,
body,
canvas {
height: 100%;
width: 100%;
margin: 0;
font-family: Helvetica;
user-select: none;
}
body {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
color: #444444;
}
h2,
h3 {
font-weight: 400;
}
h2 {
font-size: 2em;
}
h3 {
font-size: 1.3em;
}
canvas {
position: fixed;
top: 0;
width: 100%;
height: 100vh;
}
path {
opacity: 50%;
}
.content {
text-align: center;
margin: 20px 20px;
}
.paletteSelector {
z-index: 2;
margin-top: 30px;
}
.colors {
z-index: 2;
margin: 0 20px;
text-align: center;
}
.color {
height: 50px;
width: 50px;
margin-right: 10px;
border-radius: 50%;
border: none;
}
.color:hover {
opacity: .7;
cursor: pointer;
}
.color:focus {
outline: 0;
}
.rainbow-logo {
border-radius: 10%;
max-width: 40%;
max-height: 400px;
}
.signature {
margin-top: 20px;
}
#footer-container {
display: none;
}
@media only screen and (max-height: 700px) {
h2 {
font-size: 1.5em;
}
h3 {
font-size: 1.0em;
}
.color {
height: 30px;
width: 30px;
}
.signature {
margin-top: 10px;
margin-bottom: 10px;
}
.content {
margin-bottom: 10px;
}
}
</style>
</head>
<body>
<canvas width="1920" height="213"></canvas>
<div class="content">
<svg viewBox="0 0 235 234" version="1.1" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" class="rainbow-logo" preserveAspectRatio="xMinYMin meet">
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="80K">
<polygon id="Shape" fill="#88AEDC" points="234.04 175.67 158.35 233.95 205.53 233.95 234.04 212">
</polygon>
<polygon id="Shape" points="234.04 140.06 112.11 233.95 112.13 233.95 234.04 140.08"></polygon>
<polygon id="Shape" points="133.25 0.95 0.04 103.51 0.04 103.53 133.27 0.95"></polygon>
<polygon id="Shape" fill="#F58F8E" fill-rule="nonzero" points="0.04 0.95 0.04 31.11 39.21 0.95">
</polygon>
<polygon id="Shape" fill="#FEE18A" fill-rule="nonzero"
points="39.21 0.95 0.04 31.11 0.04 67.01 85.84 0.95"></polygon>
<polygon id="Shape" fill="#F3F095" fill-rule="nonzero"
points="85.84 0.95 0.04 67.01 0.04 103.51 133.25 0.95"></polygon>
<polygon id="Shape" fill="#55C1AE" fill-rule="nonzero"
points="133.27 0.95 0.04 103.53 0.04 139.12 179.49 0.95"></polygon>
<polygon id="Shape" fill="#F7B3CE" fill-rule="nonzero"
points="234.04 0.95 226.67 0.95 0.04 175.45 0.04 211.38 234.04 31.2"></polygon>
<polygon id="Shape" fill="#88AEDC" fill-rule="nonzero"
points="179.49 0.95 0.04 139.12 0.04 175.45 226.67 0.95"></polygon>
<polygon id="Shape" fill="#F58F8E" fill-rule="nonzero"
points="234.04 31.2 0.04 211.38 0.04 233.95 18.07 233.95 234.04 67.65"></polygon>
<polygon id="Shape" fill="#FEE18A" fill-rule="nonzero"
points="234.04 67.65 18.07 233.95 64.7 233.95 234.04 103.56"></polygon>
<polygon id="Shape" fill="#F3F095" fill-rule="nonzero"
points="234.04 103.56 64.7 233.95 112.11 233.95 234.04 140.06"></polygon>
<polygon id="Shape" fill="#55C1AE" fill-rule="nonzero"
points="234.04 140.08 112.13 233.95 158.35 233.95 234.04 175.67"></polygon>
<polygon id="Shape" fill="#F7B3CE" fill-rule="nonzero"
points="234.04 212 205.53 233.95 234.04 233.95"></polygon>
<g id="Group" transform="translate(37.000000, 77.000000)" fill="#FFFFFF">
<path
d="M28.2371517,0.75 C32.7510836,1.7 36.0111455,3.55 39.371517,7.05 C42.4309598,10.25 44.3368421,13.9 45.1393189,18 C45.7913313,21.45 45.7913313,58.55 45.1393189,62.05 C43.4340557,71.15 35.6600619,78.25 26.0303406,79.5 C24.0241486,79.75 17.3034056,80 11.1845201,80 L-7.10542736e-15,80 L-7.10542736e-15,1.42108547e-14 L12.4383901,1.42108547e-14 C21.2656347,1.42108547e-14 25.7795666,0.2 28.2371517,0.75 Z M14.5448916,40 L14.5448916,65.6 L19.7108359,65.4 C24.174613,65.25 25.1275542,65.05 27.1337461,63.9 C31.0458204,61.6 31.0959752,61.45 31.0959752,39.7 C31.0959752,18.5 31.0959752,18.5 27.4346749,16.1 C25.6291022,14.9 24.8767802,14.75 19.9616099,14.55 L14.5448916,14.4 L14.5448916,40 Z"
id="Combined-Shape"></path>
<path
d="M93.7894737,7.25 L93.7894737,14.5 L68.2105263,14.5 L68.2105263,32.5 L83.7585139,32.5 L83.7585139,47 L68.2105263,47 L68.3108359,56.1 L68.4613003,65.25 L81.1504644,65.4 L93.7894737,65.5 L93.7894737,80 L78.993808,80 C62.5430341,80 59.9851393,79.7 57.3770898,77.4 C53.7157895,74.2 53.9164087,76.25 53.7659443,41.1 C53.6656347,19.2 53.8160991,8.85 54.1671827,7.45 C54.8693498,4.85 57.828483,1.65 60.4365325,0.75 C61.9913313,0.2 65.9034056,0.05 78.1411765,4.26325641e-14 L93.7894737,4.26325641e-14 L93.7894737,7.25 Z"
id="Path"></path>
<path
d="M125.437152,28.1 C129.148607,42.35 132.258204,53.7 132.358514,53.35 C132.508978,53 135.668731,40.95 139.430341,26.5 L146.301548,0.25 L154.125697,0.1 C160.043963,7.10542736e-15 162,0.15 162,0.6 C162,1.05 144.64644,66.8 143.643344,70.1 C142.941176,72.4 139.179567,77.1 137.073065,78.35 C134.414861,79.85 130.502786,80.1 128.095356,78.85 C125.9387,77.75 123.079876,74.45 121.625387,71.35 C120.722601,69.45 105.97709,15.35 102.566563,1.35 L102.21548,0 L110.039628,0 C117.713313,0 117.913932,0 118.31517,1.1 C118.515789,1.75 121.725697,13.9 125.437152,28.1 Z"
id="Path"></path>
</g>
</g>
</g>
</svg>
<h2> So, there's good news and bad news</h2>
<h3>Bad news: It looks like you're offline</h3>
<h3>Good news: You can draw a picture anywhere on this page while you wait to get it back!</h3>
<h4>(pick a color below && start drawing!)</h4>
</div>
<div class="colors" style="pointer-events: all;"><button class="color"
style="background-color: rgb(244, 144, 142);"></button><button class="color"
style="background-color: rgb(242, 240, 151);"></button><button class="color"
style="background-color: rgb(136, 176, 220);"></button><button class="color"
style="background-color: rgb(247, 181, 209);"></button><button class="color"
style="background-color: rgb(83, 196, 175);"></button><button class="color"
style="background-color: rgb(253, 227, 140);"></button></div>
<div class="paletteSelector" style="pointer-events: all;">
<select>
<option value="default">Default Palette</option>
<option value="paulTol">Paul Tol Palette</option>
</select>
</div>
<svg width="75px" height="75px" viewBox="0 0 266 286" version="1.1" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" class="signature">
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Artboard-3" transform="translate(-66.000000, -39.000000)" stroke="#F4908E" stroke-width="8">
<g id="Group" transform="translate(70.000000, 44.000000)">
<path
d="M137.947881,11.0969119 C120.236214,10.7929621 127.259584,-4.63518682 110.600707,1.39809583 C98.4900537,5.78416547 5.38963223,31.9682026 1.99251552,84.34539 C0.138615017,112.929069 -0.563473858,141.639852 0.495802997,170.264134 C0.973626724,183.176113 16.3154795,204.585393 22.2134768,211.816457 C35.0843735,227.596436 61.6452079,206.201192 70.9197071,196.000824 C104.380023,159.200179 133.244668,118.47588 137.947881,67.1534449 C138.600896,60.027607 145.952931,5.41350351 126.011684,11.0969119 C112.376346,14.9830876 103.436568,65.3726702 102.950719,78.3517738 C101.357143,120.922959 86.4228272,211.00328 146.955431,222.813291 C172.852291,227.865812 169.286943,226.931981 192,226.931981"
id="Path-2"></path>
<path
d="M80,277.169785 C120.408772,252.192233 121.434199,237.685468 133.304963,222.650446 C151.591469,199.489509 173.572805,172.509583 233.39048,84.1055622 C240.794147,73.1637472 242.286005,30.9559106 238.678367,28.2608272 C236.630233,26.7307705 219.030597,13.826187 211.887732,18.9306581 C202.734845,25.4715418 203.241886,57.600262 203.223682,60.0372493 C203.010014,88.6406571 203.914879,117.248834 205.052575,145.830265 C206.126657,172.81357 211.080162,198.940616 208.995298,226.072378 C206.84232,254.090552 155.519583,271.514023 133.304963,268.076421 C126.954422,267.093706 93.5574761,243.825981 102.987596,240.93683 C113.776095,237.631506 146.615548,245.587641 160.390474,240.93683 C174.165399,236.286019 182.361602,234.057591 192.833448,229.336442 C203.454817,224.547881 249.685075,198.800486 259.8975,193.187538"
id="Path-3"></path>
</g>
</g>
</g>
</svg>
<script type="text/javascript">
const palettes = {
default: ["#F4908E", "#F2F097", "#88B0DC", "#F7B5D1", "#53C4AF", "#FDE38C"],
paulTol: ["#B997C6", "#824D99", "#4E79C4", "#57A2AC", "#7EB875", "#D0B440", "#E67F33"],
}
let activePalette = 'default'
const paletteSelector = document.querySelector(".paletteSelector")
const colorSelector = document.querySelector(".colors")
const canvas = document.querySelector('canvas')
const context = canvas.getContext('2d')
const colorDiv = document.querySelector(".colors")
let dx = 1, dy = 1;
function handleButtonClick(event) {
const buttonStyle = event.target.style
context.strokeStyle = buttonStyle.backgroundColor
}
function renderColorButtons(colors) {
colorDiv.querySelectorAll('button').forEach((button) => {
button.removeEventListener('click', handleButtonClick)
})
colorDiv.innerHTML = ""
colors.forEach(color => {
const button = document.createElement("button")
button.classList.add("color")
button.style.backgroundColor = color
colorDiv.appendChild(button)
button.addEventListener('click', handleButtonClick)
})
}
const setContextSettings = (strokeColor) => {
context.strokeStyle = strokeColor
context.lineJoin = "round"
context.lineWidth = 5
}
const setCanvasSize = () => {
canvas.setAttribute('width', window.innerWidth)
canvas.setAttribute('height', window.innerHeight)
}
const setResizeFactors = () => {
dx = canvas.width / window.innerWidth
dy = canvas.height / window.innerHeight
}
const handleResize = () => {
setCanvasSize()
setResizeFactors();
}
window.addEventListener("resize", handleResize)
const render = (palette) => {
renderColorButtons(palette)
setContextSettings(palette[0])
setCanvasSize()
}
let firstX, firstY, secondX, secondY, paint
function getCoordinates(event) {
if (["mousedown", "mousemove"].includes(event.type)) {
return [event.pageX * dx, event.pageY * dy]
} else {
return [event.touches[0].pageX * dx, event.touches[0].pageY * dy]
}
}
function addClick(event, canvas) {
let [x, y] = getCoordinates(event)
secondX = firstX
secondY = firstY
firstX = x
firstY = y
}
function draw() {
context.beginPath()
context.moveTo(secondX, secondY)
context.lineTo(firstX, firstY)
context.closePath()
context.stroke()
}
function startPaint(event) {
colorSelector.style.pointerEvents = 'none'
paletteSelector.style.pointerEvents = 'none'
addClick(event, this)
paint = true
}
canvas.addEventListener('mousedown', startPaint)
canvas.addEventListener('touchstart', startPaint)
const exit = _ => {
colorSelector.style.pointerEvents = 'all'
paletteSelector.style.pointerEvents = 'all'
paint = false
firstX = null
firstY = null
}
canvas.addEventListener('mouseup', exit)
canvas.addEventListener('mouseleave', exit)
canvas.addEventListener('touchend', exit)
function endPaint(event) {
if (paint) {
addClick(event, this)
draw()
}
}
canvas.addEventListener('mousemove', endPaint)
canvas.addEventListener('touchmove', endPaint)
paletteSelector.onchange = (event) => {
activePalette = event.target.value
render(palettes[activePalette])
}
(function () {
render(palettes[activePalette])
})()
</script>
</body>
</html>