Autohandsketch / index.html
luigi12345's picture
Update index.html
eeaea49 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Hand-Sketch Animation</title>
<style>
body {
margin: 0;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background-color: #f0f0f0;
}
#container {
position: relative;
display: flex;
justify-content: center;
align-items: center;
width: 100%; /* Set the width to 100% of the viewport */
}
#canvas-wrapper {
flex: 1; /* Make it take up available space */
display: flex;
justify-content: center;
align-items: center;
position: relative;
}
#book-wrapper {
flex:1;
display: flex;
justify-content: center;
align-items: center;
position: relative;
}
canvas {
display: block;
background-color: white;
border: 2px solid black;
box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.3);
position: absolute;
}
#hand {
position: absolute;
width: 100px;
height: auto;
pointer-events: none;
transform: translate(-50%,-50%);
}
#book {
width: 400px;
height: auto;
pointer-events: none;
transform: translate(-50%,-50%);
}
#character1 {
position: absolute;
width: 100px;
height: auto;
display: none;
transform: translate(-50%,-50%);
}
#character2 {
position: absolute;
width: 100px;
height: auto;
display: none;
transform: translate(-50%,-50%);
}
#character3 {
position: absolute;
width: 100px;
height: auto;
display: none;
transform: translate(-50%,-50%);
}
.doodle-text {
position: absolute;
font-size: 0.8rem; /* Adjust as needed */
white-space: nowrap; /* Prevent wrapping */
transform: translate(-50%,-50%);
}
#character1-text {
left: -20px;
top: 30px;
}
#character2-text {
left: -20px;
top: 30px;
}
#character3-text {
left: -20px;
top: 30px;
}
</style>
</head>
<body>
<div id="container">
<div id="book-wrapper">
<img id="book" src="https://i.ibb.co/RpZQqFCM/240-F-78487923-cord-V8-UB0-ZUyu3-WRb-LG4-IRVCP65-BExyh.webp">
<img id="hand" src="https://cdn.pixabay.com/photo/2016/12/08/06/40/writing-1891073_1280.png">
</div>
<div id="canvas-wrapper">
<canvas id="sketchCanvas"></canvas>
</div>
<img id="character1" src="https://upload.wikimedia.org/wikipedia/en/6/65/Arthur_Read.svg" alt="Character 1">
<div id="character1-text" class="doodle-text">
{ { x 4 } 2 4
</div>
<img id="character2" src="https://upload.wikimedia.org/wikipedia/en/6/65/Arthur_Read.svg" alt="Character 2">
<div id="character2-text" class="doodle-text">
} { 3 3 5 g
</div>
<img id="character3" src="https://upload.wikimedia.org/wikipedia/en/6/65/Arthur_Read.svg" alt="Character 3">
<div id="character3-text" class="doodle-text">
a g 9
</div>
</div>
<script>
const container = document.getElementById('container');
const canvasWrapper = document.getElementById('canvas-wrapper');
const bookWrapper = document.getElementById('book-wrapper');
const canvas = document.getElementById('sketchCanvas');
const ctx = canvas.getContext('2d');
const hand = document.getElementById('hand');
const bookImg = document.getElementById('book');
// Get bounding rectangles once and reuse
let canvasRect, handRect, containerRect, bookRect, bookWrapperRect, canvasWrapperRect;
// Function to update bounding rectangles if needed
const updateBoundingRects = () => {
canvasRect = canvas.getBoundingClientRect();
handRect = hand.getBoundingClientRect();
bookRect = bookImg.getBoundingClientRect();
bookWrapperRect = bookWrapper.getBoundingClientRect();
canvasWrapperRect = canvasWrapper.getBoundingClientRect();
}
const character1 = document.getElementById('character1');
const character2 = document.getElementById('character2');
const character3 = document.getElementById('character3');
let screenWidth = 0;
let canvasWidth = 0;
let canvasHeight = 0;
let currentPosition = {x:0, y:0};
const aspectRatio = bookImg.width/bookImg.height;
const bookWidth = 400;
const bookHeight = bookWidth/aspectRatio;
const text = [
{
text: "THE 48 LAWS",
x: (bookWidth*0.25),
y: (bookHeight*0.2),
},
{
text: "OF",
x: (bookWidth*0.25),
y: (bookHeight*0.3),
},
{
text: "POWER",
x: (bookWidth*0.25),
y: (bookHeight*0.4),
},
{
text: "by Robert",
x: (bookWidth*0.25),
y: (bookHeight*0.6),
},
{
text: "Greene",
x: (bookWidth*0.25),
y: (bookHeight*0.7),
},
{
text: "LAW #33",
x: (bookWidth*0.6),
y: (bookHeight*0.2),
},
{
text: "DISCOVER",
x: (bookWidth*0.6),
y: (bookHeight*0.3),
},
{
text: "EACH MAN'S",
x: (bookWidth*0.6),
y: (bookHeight*0.4),
},
{
text: "THUMBSCREW",
x: (bookWidth*0.6),
y: (bookHeight*0.5),
},
];
const text2 = [
{
text: "Everyone has a weakness, a gap in the castle wall.",
x: 10,
y: 50
},
{
text: "That weakness is usually insecurity, an uncontrollable",
x: 10,
y: 70
},
{
text: "emotion or need; it can also be a small secret pleasure.",
x: 10,
y: 90
},
{
text: "Either way, once found, it is a thumbscrew you can turn to",
x: 10,
y: 110
},
{
text: "your advantage.",
x: 10,
y: 130
},
{
text: "One of the most important things to realize about people",
x: 10,
y: 170
},
{
text: "is that they all have a weakness, some part of their",
x: 10,
y: 190
},
{
text: "psychological armor that will not resist, that will",
x: 10,
y: 210
},
{
text: "bend to your will if you find it and push on it.",
x: 10,
y: 230
},
{
text: "Some people wear their weaknesses openly, others disguise",
x: 10,
y: 250
},
{
text: "them. Those who disguise them are often the ones most",
x: 10,
y: 270
},
{
text: "effectively undone through that one chink in their armor.",
x: 10,
y: 290
},
];
const text3 = [
{
text:"Richelieu",
x: 50,
y: 370
},
{
text:"Louis XIII",
x: 200,
y: 370
},
{
text:"Marie de' Medicis",
x: 350,
y: 370
}
];
let currentTextIndex = 0;
let currentLetterIndex = 0;
let currentLineIndex = 0;
const strokeStyle = 'black';
const lineWidth = 1;
function getRandomNumber(min, max) {
return Math.random() * (max - min) + min;
}
function generatePointsFromText(txt, startX, startY) {
const points = [];
const letters = txt.split("");
let currentX = startX;
const letterSpacing = 10;
for(const letter of letters) {
const letterPaths = getLetterPaths(letter);
for (const path of letterPaths) {
let path_points = []
for (let i = 0; i < path.length; i += 2) {
path_points.push({x: currentX + path[i], y: startY + path[i+1]});
}
points.push(path_points)
}
currentX += letterSpacing + getLetterWidth(letter);
}
return points;
}
function drawLine(x1, y1, x2, y2) {
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.stroke();
}
function drawSketchLine(x1, y1, x2, y2) {
const numPoints = 10; //Number of intermediate points for more smoothness
ctx.beginPath();
ctx.moveTo(x1, y1);
for(let i = 1; i < numPoints; i++) {
const t = i/numPoints;
let x = x1 + (x2 - x1) * t;
let y = y1 + (y2 - y1) * t;
//jitter effect
x += getRandomNumber(-lineWidth, lineWidth) * 2;
y += getRandomNumber(-lineWidth, lineWidth)*2;
ctx.lineTo(x, y);
}
ctx.lineTo(x2, y2);
ctx.stroke();
}
let allTextPoints = [];
function animateText() {
updateCanvasSize();
updateBoundingRects();
ctx.clearRect(0,0, canvas.width, canvas.height);
ctx.strokeStyle = strokeStyle;
ctx.lineWidth = lineWidth;
if(allTextPoints.length === 0) {
allTextPoints = text.map((t) => {
return {points:generatePointsFromText(t.text, t.x, t.y), index:0}
});
}
if(currentTextIndex < allTextPoints.length) {
let currentAnimatedText = allTextPoints[currentTextIndex];
let points = currentAnimatedText.points;
if (currentAnimatedText.index < points.length) {
let path = points[currentAnimatedText.index];
if(currentLineIndex < path.length -1 ) {
let x1 = path[currentLineIndex].x
let y1 = path[currentLineIndex].y;
let x2 = path[currentLineIndex+1].x
let y2 = path[currentLineIndex+1].y;
// update the position of hand in relation to the current animation
hand.style.left = (x1 + canvasWrapperRect.left) + 'px';
hand.style.top = (y1 + canvasWrapperRect.top ) + 'px';
drawSketchLine(x1, y1, x2, y2);
currentLineIndex++;
requestAnimationFrame(animateText);
return;
} else {
currentLineIndex = 0;
currentAnimatedText.index++;
requestAnimationFrame(animateText);
return;
}
} else {
currentTextIndex++;
currentLineIndex = 0;
requestAnimationFrame(animateText);
return;
}
} else {
currentTextIndex = 0;
currentLetterIndex = 0;
currentLineIndex = 0;
allTextPoints = [];
hand.style.display = 'none';
ctx.clearRect(0,0, canvas.width, canvas.height);
allTextPoints = text2.map((t) => {
return {points:generatePointsFromText(t.text, t.x, t.y), index:0}
});
requestAnimationFrame(animateText2);
return;
}
}
function animateText2() {
updateCanvasSize();
updateBoundingRects();
ctx.clearRect(0,0, canvas.width, canvas.height);
ctx.strokeStyle = strokeStyle;
ctx.lineWidth = lineWidth;
if(allTextPoints.length === 0) {
allTextPoints = text2.map((t) => {
return {points:generatePointsFromText(t.text, t.x, t.y), index:0}
});
}
if(currentTextIndex < allTextPoints.length) {
let currentAnimatedText = allTextPoints[currentTextIndex];
let points = currentAnimatedText.points;
if (currentAnimatedText.index < points.length) {
let path = points[currentAnimatedText.index];
if(currentLineIndex < path.length -1 ) {
let x1 = path[currentLineIndex].x
let y1 = path[currentLineIndex].y;
let x2 = path[currentLineIndex+1].x
let y2 = path[currentLineIndex+1].y;
// update the position of hand in relation to the current animation
hand.style.left = (x1 + canvasWrapperRect.left) + 'px';
hand.style.top = (y1 + canvasWrapperRect.top ) + 'px';
drawSketchLine(x1, y1, x2, y2);
currentLineIndex++;
requestAnimationFrame(animateText2);
return;
} else {
currentLineIndex = 0;
currentAnimatedText.index++;
requestAnimationFrame(animateText2);
return;
}
} else {
currentTextIndex++;
currentLineIndex = 0;
requestAnimationFrame(animateText2);
return;
}
} else {
currentTextIndex = 0;
currentLetterIndex = 0;
currentLineIndex = 0;
allTextPoints = [];
hand.style.display = 'none';
ctx.clearRect(0,0, canvas.width, canvas.height);
character1.style.display='inline';
character2.style.display='inline';
character3.style.display='inline';
character1.style.left = canvasWrapperRect.left + 'px';
character1.style.top = canvasWrapperRect.top + (bookHeight*0.8) + 'px';
character2.style.left = (canvasWrapperRect.left + canvasWrapperRect.width * 0.35)+ 'px';
character2.style.top = canvasWrapperRect.top + (bookHeight*0.8) + 'px';
character3.style.left = (canvasWrapperRect.left + canvasWrapperRect.width * 0.70) + 'px';
character3.style.top = canvasWrapperRect.top + (bookHeight*0.8) + 'px';
allTextPoints = text3.map((t) => {
return {points:generatePointsFromText(t.text, t.x, t.y), index:0}
});
requestAnimationFrame(animateText3);
return;
}
}
function animateText3() {
updateBoundingRects();
ctx.strokeStyle = strokeStyle;
ctx.lineWidth = lineWidth;
if(allTextPoints.length === 0) {
allTextPoints = text3.map((t) => {
return {points:generatePointsFromText(t.text, t.x, t.y), index:0}
});
}
if(currentTextIndex < allTextPoints.length) {
let currentAnimatedText = allTextPoints[currentTextIndex];
let points = currentAnimatedText.points;
if (currentAnimatedText.index < points.length) {
let path = points[currentAnimatedText.index];
if(currentLineIndex < path.length -1 ) {
let x1 = path[currentLineIndex].x
let y1 = path[currentLineIndex].y;
let x2 = path[currentLineIndex+1].x
let y2 = path[currentLineIndex+1].y;
// update the position of hand in relation to the current animation
hand.style.left = (x1 + canvasWrapperRect.left) + 'px';
hand.style.top = (y1 + canvasWrapperRect.top ) + 'px';
drawSketchLine(x1, y1, x2, y2);
currentLineIndex++;
requestAnimationFrame(animateText3);
return;
} else {
currentLineIndex = 0;
currentAnimatedText.index++;
requestAnimationFrame(animateText3);
return;
}
} else {
currentTextIndex++;
currentLineIndex = 0;
requestAnimationFrame(animateText3);
return;
}
} else {
hand.style.display = 'none';
return;
}
}
function updateCanvasSize() {
screenWidth = window.innerWidth;
canvasWidth = screenWidth*0.45;
canvasHeight = canvasWidth / aspectRatio ;
canvas.width = canvasWidth ;
canvas.height = canvasHeight;
bookImg.style.width = (screenWidth * 0.45) + 'px';
hand.style.left = (bookWidth*0.2) + 'px' ;
hand.style.top = (bookHeight*0.2) + 'px';
}
window.onload = () => {
updateCanvasSize();
updateBoundingRects();
hand.style.display = 'inline';
animateText();
}
window.addEventListener('resize', () => {
updateCanvasSize();
updateBoundingRects();
});
// ---- DATA OF CHARACTERS OF LETTERS -------
function getLetterPaths(letter) {
switch(letter){
case 'A':
return [
[0, 10, 10, 0, 20,10],
[5, 7, 15, 7]
];
case 'B':
return [
[0,0, 0, 10, 10, 10, 10, 5, 0,5],
[10, 5, 10, 0, 0, 0]
];
case 'C':
return [
[10, 0, 0, 0, 0, 10, 10, 10]
];
case 'D':
return [
[0, 0, 0, 10, 10, 10, 10, 0, 0, 0]
];
case 'E':
return [
[0,0, 0,10],
[0, 0, 10, 0],
[0,5, 7,5],
[0, 10, 10, 10],
];
case 'F':
return [
[0,0, 0, 10],
[0,0, 10, 0],
[0,5, 7,5],
];
case 'G':
return [
[10, 0, 0, 0, 0, 10, 10, 10],
[10, 10, 10, 5]
];
case 'H':
return [
[0, 0, 0, 10],
[10,0, 10, 10],
[0,5, 10, 5],
];
case 'I':
return [
[5,0, 5, 10]
];
case 'J':
return [
[7,0,7,10, 0, 10]
];
case 'K':
return [
[0,0, 0, 10],
[0,5, 10,0],
[0,5, 10, 10],
];
case 'L':
return [
[0, 0, 0, 10],
[0, 10, 10, 10],
];
case 'M':
return [
[0, 10, 0,0, 10,5, 20,0, 20, 10],
];
case 'N':
return [
[0, 10, 0,0, 10, 10, 10, 0]
];
case 'O':
return [
[10, 0, 0, 0, 0, 10, 10, 10, 10,0]
];
case 'P':
return [
[0,0, 0, 10, 10, 10, 10, 5, 0,5]
];
case 'Q':
return [
[10, 0, 0, 0, 0, 10, 10, 10, 10,0],
[10, 5, 15, 10]
];
case 'R':
return [
[0,0, 0, 10, 10, 10, 10, 5, 0,5],
[10,5, 20, 10]
];
case 'S':
return [
[10, 0, 0, 0, 0, 5, 10,5, 10, 10, 0, 10]
];
case 'T':
return [
[5, 0, 5, 10],
[0, 0, 10, 0],
];
case 'U':
return [
[0, 0, 0, 10, 10, 10, 10,0]
];
case 'V':
return [
[0,0, 5,10, 10,0],
];
case 'W':
return [
[0,0, 5,10, 10, 0, 15, 10, 20,0]
];
case 'X':
return [
[0,0, 10, 10],
[10,0, 0, 10]
];
case 'Y':
return [
[5, 0, 5,5, 0, 10],
[5,5, 10, 10]
];
case 'Z':
return [
[0,0, 10,0, 0,10, 10, 10]
];
case ' ':
return [
[]
]
default:
return []
}
}
function getLetterWidth(letter) {
switch(letter){
case 'A': return 20;
case 'B': return 10;
case 'C': return 10;
case 'D': return 10;
case 'E': return 10;
case 'F': return 10;
case 'G': return 10;
case 'H': return 10;
case 'I': return 5;
case 'J': return 7;
case 'K': return 10;
case 'L': return 10;
case 'M': return 20;
case 'N': return 10;
case 'O': return 10;
case 'P': return 10;
case 'Q': return 15;
case 'R': return 20;
case 'S': return 10;
case 'T': return 10;
case 'U': return 10;
case 'V': return 10;
case 'W': return 20;
case 'X': return 10;
case 'Y': return 10;
case 'Z': return 10;
default: return 0;
}
}
</script>
</body>
</html>