Animations

This commit is contained in:
2025-08-27 08:27:22 -07:00
parent 1758dc3153
commit 12631dbd42
2 changed files with 610 additions and 73 deletions

View File

@@ -1,3 +1,4 @@
// src/components/resources/presentation.astro
<button id="presentation-button" class="presentation-hidden" type="button"
>Start Presentation</button
>
@@ -12,6 +13,7 @@
let slides = Array.from(document.querySelectorAll(".presentation-slide"));
let slide = 0;
let step = 0; // Current step within the slide
let presenter = false;
const presentationId = window.location.href;
@@ -20,7 +22,6 @@
if (slide === slides.length - 1) {
return slide;
}
return slide + 1;
};
@@ -28,7 +29,6 @@
if (slide === 0) {
return slide;
}
return slide - 1;
};
@@ -38,9 +38,182 @@
const transitionClasses = [nextClass, currClass, prevClass];
const keyHandlers: Record<string, () => number> = {
ArrowRight: nextSlide,
ArrowLeft: prevSlide,
// Animation classes for step-through content
const animationClasses = {
'fade-in': 'animate-fade-in-step',
'slide-in-left': 'animate-slide-in-left-step',
'slide-in-right': 'animate-slide-in-right-step',
'slide-in-up': 'animate-slide-in-up-step',
'slide-in-down': 'animate-slide-in-down-step',
'type-in': 'animate-type-in-step',
'scale-in': 'animate-scale-in-step',
'bounce-in': 'animate-bounce-in-step'
};
const getCurrentSlideSteps = () => {
const currentSlide = slides[slide];
return Array.from(currentSlide.querySelectorAll('[step]'))
.sort((a, b) => {
const stepA = parseInt((a as HTMLElement).getAttribute('step') || '0');
const stepB = parseInt((b as HTMLElement).getAttribute('step') || '0');
return stepA - stepB;
});
};
const getMaxSteps = () => {
const steps = getCurrentSlideSteps();
if (steps.length === 0) return 0;
const lastStep = steps[steps.length - 1] as HTMLElement;
return parseInt(lastStep.getAttribute('step') || '0');
};
const showStepsUpTo = (targetStep: number, isReverse: boolean = false) => {
const steps = getCurrentSlideSteps();
steps.forEach((stepElement) => {
const element = stepElement as HTMLElement;
const elementStep = parseInt(element.getAttribute('step') || '0');
const animationType = element.getAttribute('animation') || 'fade-in';
// Remove all animation classes first
Object.values(animationClasses).forEach(cls => {
element.classList.remove(cls);
element.classList.remove(cls.replace('-step', '-reverse-step'));
});
element.classList.remove('step-hidden', 'step-visible');
if (elementStep <= targetStep) {
// Show this step with animation
element.classList.add('step-visible');
if (elementStep === targetStep) {
// Apply animation only to the current step
const baseAnimationClass = animationClasses[animationType as keyof typeof animationClasses] || animationClasses['fade-in'];
const animationClass = isReverse ? baseAnimationClass.replace('-step', '-reverse-step') : baseAnimationClass;
// Special handling for type-in animation
if (animationType === 'type-in') {
if (isReverse) {
startTypeAnimation(element, false);
} else {
startTypeAnimation(element, true);
}
} else {
element.classList.add(animationClass);
}
}
} else {
// Hide this step
element.classList.add('step-hidden');
}
});
};
const startTypeAnimation = (element: HTMLElement, isForward: boolean) => {
const text = element.textContent || '';
const duration = isForward ? 1500 : 1000; // ms
const steps = Math.max(text.length, 1);
const stepDuration = duration / steps;
if (isForward) {
// Type in: reveal characters one by one
element.textContent = '';
element.style.opacity = '1';
let currentIndex = 0;
const typeInterval = setInterval(() => {
if (currentIndex < text.length) {
element.textContent = text.substring(0, currentIndex + 1);
currentIndex++;
} else {
clearInterval(typeInterval);
}
}, stepDuration);
// Store interval reference for cleanup
(element as any)._typeInterval = typeInterval;
} else {
// Type out: hide characters one by one from the end
let currentLength = text.length;
const typeInterval = setInterval(() => {
if (currentLength > 0) {
element.textContent = text.substring(0, currentLength - 1);
currentLength--;
} else {
clearInterval(typeInterval);
element.style.opacity = '0';
}
}, stepDuration);
// Store interval reference for cleanup
(element as any)._typeInterval = typeInterval;
}
};
const resetSlideSteps = () => {
const steps = getCurrentSlideSteps();
steps.forEach((stepElement) => {
const element = stepElement as HTMLElement;
// Clear any running type animations
if ((element as any)._typeInterval) {
clearInterval((element as any)._typeInterval);
(element as any)._typeInterval = null;
}
Object.values(animationClasses).forEach(cls => {
element.classList.remove(cls);
element.classList.remove(cls.replace('-step', '-reverse-step'));
});
element.classList.remove('step-hidden', 'step-visible');
element.classList.add('step-hidden');
// Reset text content and styles for type-in elements
if (element.getAttribute('animation') === 'type-in') {
element.style.opacity = '0';
}
});
};
const nextStep = () => {
const maxSteps = getMaxSteps();
if (step < maxSteps) {
step++;
showStepsUpTo(step, false);
return { slide, step };
} else {
// Move to next slide
if (slide < slides.length - 1) {
const nextSlideIndex = nextSlide();
return { slide: nextSlideIndex, step: 0 };
}
return { slide, step };
}
};
const prevStep = () => {
if (step > 0) {
step--;
showStepsUpTo(step, true);
return { slide, step };
} else {
// Move to previous slide
if (slide > 0) {
const prevSlideIndex = prevSlide();
// Set to max steps of previous slide
const tempSlide = slide;
slide = prevSlideIndex;
const maxSteps = getMaxSteps();
slide = tempSlide;
return { slide: prevSlideIndex, step: maxSteps };
}
return { slide, step };
}
};
const keyHandlers: Record<string, () => { slide: number, step: number }> = {
ArrowRight: nextStep,
ArrowLeft: prevStep,
};
const displaySlides = () => {
@@ -49,8 +222,15 @@
if (i === slide) {
slides[i].classList.add("active", currClass);
// Show steps for current slide
showStepsUpTo(step, false);
} else {
slides[i].classList.add("inactive");
// Reset steps for non-current slides
const tempSlide = slide;
slide = i;
resetSlideSteps();
slide = tempSlide;
if (i > slide) {
slides[i].classList.add(nextClass);
@@ -61,62 +241,89 @@
}
};
let presenting = false
let presenting = false;
const startPresentation = () => {
button.innerHTML = "Resume presentation";
document.body.classList.add("presentation-overflow-hidden");
presenting = true
presenting = true;
step = 0; // Reset step
displaySlides();
setProgress();
initListeners()
initListeners();
};
const endPresentation = () => {
document.body.classList.remove("presentation-overflow-hidden");
presenting = false
slides.map((s) =>
s.classList.remove("active", "inactive", ...transitionClasses)
);
presenting = false;
step = 0;
slides.map((s) => {
s.classList.remove("active", "inactive", ...transitionClasses);
// Reset all steps
const tempSlide = slide;
slides.forEach((_, i) => {
slide = i;
resetSlideSteps();
});
slide = tempSlide;
});
};
const setPresenter = () => {
presenter = true;
document.body.classList.add("presentation-presenter")
document.body.classList.add("presentation-presenter");
};
const setProgress = () => {
const progress = ((slide+1)/slides.length)*100;
document.body.style.setProperty('--presentation-progress', `${progress}%`)
}
const maxSteps = getMaxSteps();
const totalSteps = slides.reduce((acc, _, i) => {
const tempSlide = slide;
slide = i;
const steps = getMaxSteps();
slide = tempSlide;
return acc + Math.max(1, steps);
}, 0);
const transition = (nextSlide: number) => {
let currentProgress = 0;
for (let i = 0; i < slide; i++) {
const tempSlide = slide;
slide = i;
const steps = getMaxSteps();
slide = tempSlide;
currentProgress += Math.max(1, steps);
}
currentProgress += step;
const progress = (currentProgress / totalSteps) * 100;
document.body.style.setProperty('--presentation-progress', `${progress}%`);
};
const transition = (nextSlide: number, nextStep: number = 0) => {
if (!presenting) {
return
return;
}
if (slide === nextSlide) {
if (slide === nextSlide && step === nextStep) {
return;
}
slides.forEach((s) => s.classList.remove(...transitionClasses));
slide = nextSlide;
step = nextStep;
displaySlides();
setProgress();
};
let listenersInitialized = false
let listenersInitialized = false;
const initListeners = () => {
if (listenersInitialized) {
return
return;
}
listenersInitialized= true
listenersInitialized = true;
window.addEventListener("keyup", (ev) => {
ev.preventDefault();
const isEscape = ev.key === "Escape";
@@ -131,19 +338,19 @@
return;
}
const getSlide = keyHandlers[ev.key];
const getNextPosition = keyHandlers[ev.key];
if (!getSlide) {
if (!getNextPosition) {
return;
}
const nextSlide = getSlide();
transition(nextSlide);
const { slide: nextSlideIndex, step: nextStepIndex } = getNextPosition();
transition(nextSlideIndex, nextStepIndex);
});
let touchstartX = 0;
let touchendX = 0;
const handleGesure = () => {
const handleGesture = () => {
const magnitude = Math.abs(touchstartX - touchendX);
if (magnitude < 40) {
@@ -152,10 +359,12 @@
}
if (touchendX < touchstartX) {
transition(nextSlide());
const { slide: nextSlideIndex, step: nextStepIndex } = nextStep();
transition(nextSlideIndex, nextStepIndex);
}
if (touchendX > touchstartX) {
transition(prevSlide());
const { slide: prevSlideIndex, step: prevStepIndex } = prevStep();
transition(prevSlideIndex, prevStepIndex);
}
};
@@ -171,11 +380,11 @@
"touchend",
(event) => {
touchendX = event.changedTouches[0].screenX;
handleGesure();
handleGesture();
},
false
);
}
};
// If there is no presentation on the page then we don't initialize
if (slides.length) {
@@ -189,6 +398,259 @@
display: none;
}
/* Step animation styles */
.step-hidden {
opacity: 0;
visibility: hidden;
}
.step-visible {
opacity: 1;
visibility: visible;
}
/* Animation keyframes */
@keyframes fadeInStep {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes fadeOutStep {
from {
opacity: 1;
}
to {
opacity: 0;
}
}
@keyframes slideInLeftStep {
from {
opacity: 0;
transform: translateX(-30px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
@keyframes slideOutLeftStep {
from {
opacity: 1;
transform: translateX(0);
}
to {
opacity: 0;
transform: translateX(-30px);
}
}
@keyframes slideInRightStep {
from {
opacity: 0;
transform: translateX(30px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
@keyframes slideOutRightStep {
from {
opacity: 1;
transform: translateX(0);
}
to {
opacity: 0;
transform: translateX(30px);
}
}
@keyframes slideInUpStep {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes slideOutUpStep {
from {
opacity: 1;
transform: translateY(0);
}
to {
opacity: 0;
transform: translateY(30px);
}
}
@keyframes slideInDownStep {
from {
opacity: 0;
transform: translateY(-30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes slideOutDownStep {
from {
opacity: 1;
transform: translateY(0);
}
to {
opacity: 0;
transform: translateY(-30px);
}
}
@keyframes scaleInStep {
from {
opacity: 0;
transform: scale(0.8);
}
to {
opacity: 1;
transform: scale(1);
}
}
@keyframes scaleOutStep {
from {
opacity: 1;
transform: scale(1);
}
to {
opacity: 0;
transform: scale(0.8);
}
}
@keyframes bounceInStep {
0% {
opacity: 0;
transform: scale(0.3);
}
50% {
opacity: 1;
transform: scale(1.05);
}
70% {
transform: scale(0.9);
}
100% {
opacity: 1;
transform: scale(1);
}
}
@keyframes bounceOutStep {
0% {
opacity: 1;
transform: scale(1);
}
30% {
transform: scale(1.05);
}
50% {
opacity: 1;
transform: scale(0.9);
}
100% {
opacity: 0;
transform: scale(0.3);
}
}
@keyframes typeInStep {
/* This is now unused - keeping for backward compatibility */
from { opacity: 1; }
to { opacity: 1; }
}
@keyframes typeOutStep {
/* This is now unused - keeping for backward compatibility */
from { opacity: 1; }
to { opacity: 1; }
}
/* Animation classes */
.animate-fade-in-step {
animation: fadeInStep 0.6s ease-out forwards;
}
.animate-fade-in-reverse-step {
animation: fadeOutStep 0.6s ease-out forwards;
}
.animate-slide-in-left-step {
animation: slideInLeftStep 0.6s ease-out forwards;
}
.animate-slide-in-left-reverse-step {
animation: slideOutLeftStep 0.6s ease-out forwards;
}
.animate-slide-in-right-step {
animation: slideInRightStep 0.6s ease-out forwards;
}
.animate-slide-in-right-reverse-step {
animation: slideOutRightStep 0.6s ease-out forwards;
}
.animate-slide-in-up-step {
animation: slideInUpStep 0.6s ease-out forwards;
}
.animate-slide-in-up-reverse-step {
animation: slideOutUpStep 0.6s ease-out forwards;
}
.animate-slide-in-down-step {
animation: slideInDownStep 0.6s ease-out forwards;
}
.animate-slide-in-down-reverse-step {
animation: slideOutDownStep 0.6s ease-out forwards;
}
.animate-scale-in-step {
animation: scaleInStep 0.6s ease-out forwards;
}
.animate-scale-in-reverse-step {
animation: scaleOutStep 0.6s ease-out forwards;
}
.animate-bounce-in-step {
animation: bounceInStep 0.8s ease-out forwards;
}
.animate-bounce-in-reverse-step {
animation: bounceOutStep 0.8s ease-out forwards;
}
.animate-type-in-step {
/* JavaScript-controlled animation - no CSS animation needed */
}
.animate-type-in-reverse-step {
/* JavaScript-controlled animation - no CSS animation needed */
}
.presentation-overflow-hidden {
overflow: hidden;
visibility: hidden;

View File

@@ -14,84 +14,99 @@ import Presentation from "@/components/resources/presentation.astro";
<Slide large>
# Welcome to Python! 🐍
**A beginner-friendly programming language**
<div step="1" animation="fade-in">**A beginner-friendly programming language**</div>
- Easy to learn and read
- Powerful and versatile
- Great for beginners
- Used by major companies
<div step="2" animation="slide-in-left">- Easy to learn and read</div>
<div step="3" animation="slide-in-left">- Powerful and versatile</div>
<div step="4" animation="slide-in-left">- Great for beginners</div>
<div step="5" animation="bounce-in">- Used by major companies</div>
</Slide>
<Slide>
## What is Python?
Python is a **high-level programming language** that emphasizes:
<div step="1" animation="fade-in">Python is a **high-level programming language** that emphasizes:</div>
- **Readability** - Code that looks like English
- **Simplicity** - Easy to learn and use
- **Versatility** - Can be used for many things
<div step="2" animation="slide-in-up">- **Readability** - Code that looks like English</div>
<div step="3" animation="slide-in-up">- **Simplicity** - Easy to learn and use</div>
<div step="4" animation="slide-in-up">- **Versatility** - Can be used for many things</div>
### Created by Guido van Rossum in 1991
<div step="5" animation="scale-in">### Created by Guido van Rossum in 1991</div>
Named after "Monty Python's Flying Circus" 🎭
<div step="6" animation="type-in">Named after "Monty Python's Flying Circus" 🎭</div>
</Slide>
<Slide>
## Why Learn Python?
<div step="1" animation="slide-in-left">
### 🌐 Web Development
Build websites and web applications
</div>
<div step="2" animation="slide-in-right">
### 🤖 Data Science & AI
Analyze data and build machine learning models
</div>
<div step="3" animation="slide-in-left">
### 🎮 Game Development
Create games and interactive applications
</div>
<div step="4" animation="slide-in-right">
### 🔧 Automation
Automate repetitive tasks
</div>
</Slide>
<Slide>
## Your First Python Program
Let's write the classic "Hello, World!" program:
<div step="1" animation="fade-in">Let's write the classic "Hello, World!" program:</div>
<div step="2" animation="scale-in">
```python
print("Hello, World!")
```
</div>
### How to run it:
1. Open a text editor
2. Type the code above
3. Save as `hello.py`
4. Run with: `python hello.py`
<div step="3" animation="slide-in-up">### How to run it:</div>
<div step="4" animation="slide-in-up">1. Open a text editor</div>
<div step="5" animation="slide-in-up">2. Type the code above</div>
<div step="6" animation="slide-in-up">3. Save as `hello.py`</div>
<div step="7" animation="bounce-in">4. Run with: `python hello.py`</div>
</Slide>
<Slide>
## Variables and Data Types
### Creating Variables
<div step="1" animation="fade-in">### Creating Variables</div>
<div step="2" animation="type-in">
```python
name = "Alice" # String
age = 25 # Integer
height = 5.6 # Float
is_student = True # Boolean
```
</div>
### Python is **dynamically typed**
You don't need to declare the type!
<div step="3" animation="slide-in-left">### Python is **dynamically typed**</div>
<div step="4" animation="slide-in-left">You don't need to declare the type!</div>
<div step="5" animation="scale-in">
```python
x = 42 # x is an integer
x = "Hello" # Now x is a string
```
</div>
</Slide>
<Slide>
## Working with Strings
### String Operations
<div data-step="1" data-animation="fade-in">### String Operations</div>
<div data-step="2" data-animation="slide-in-up">
```python
# Creating strings
greeting = "Hello"
@@ -100,47 +115,72 @@ name = "World"
# Concatenation
message = greeting + ", " + name + "!"
print(message) # Output: Hello, World!
```
</div>
<div data-step="3" data-animation="slide-in-up">
```python
# String methods
text = "python programming"
print(text.upper()) # PYTHON PROGRAMMING
print(text.capitalize()) # Python programming
print(text.replace("python", "Python")) # Python programming
```
</div>
</Slide>
<Slide>
## Numbers and Math
### Basic Math Operations
<div data-step="1" data-animation="fade-in">### Basic Math Operations</div>
<div data-step="2" data-animation="type-in">
```python
# Arithmetic operators
a = 10
b = 3
```
</div>
<div data-step="3" data-animation="slide-in-left">
```python
print(a + b) # Addition: 13
print(a - b) # Subtraction: 7
print(a * b) # Multiplication: 30
print(a / b) # Division: 3.333...
```
</div>
<div data-step="4" data-animation="slide-in-right">
```python
print(a // b) # Floor division: 3
print(a % b) # Modulo (remainder): 1
print(a ** b) # Exponentiation: 1000
```
</div>
</Slide>
<Slide>
## Lists - Storing Multiple Values
### Creating and Using Lists
<div data-step="1" data-animation="fade-in">### Creating and Using Lists</div>
<div data-step="2" data-animation="slide-in-up">
```python
# Creating a list
fruits = ["apple", "banana", "orange"]
numbers = [1, 2, 3, 4, 5]
```
</div>
<div data-step="3" data-animation="slide-in-up">
```python
# Accessing items (indexing starts at 0)
print(fruits[0]) # apple
print(fruits[-1]) # orange (last item)
```
</div>
<div data-step="4" data-animation="slide-in-up">
```python
# Adding items
fruits.append("grape")
fruits.insert(1, "strawberry")
@@ -149,12 +189,14 @@ fruits.insert(1, "strawberry")
print(len(fruits)) # Length of list
print("apple" in fruits) # Check if item exists
```
</div>
</Slide>
<Slide>
## Control Flow - Making Decisions
### If Statements
<div data-step="1" data-animation="fade-in">### If Statements</div>
<div data-step="2" data-animation="type-in">
```python
age = 18
@@ -164,65 +206,89 @@ elif age >= 16:
print("You can drive!")
else:
print("You're still young!")
```
</div>
<div data-step="3" data-animation="bounce-in">
```python
# Comparison operators
# == (equal), != (not equal)
# > (greater), < (less)
# >= (greater or equal), <= (less or equal)
```
</div>
</Slide>
<Slide>
## Loops - Repeating Actions
### For Loops
<div data-step="1" data-animation="fade-in">### For Loops</div>
<div data-step="2" data-animation="slide-in-left">
```python
# Loop through a list
fruits = ["apple", "banana", "orange"]
for fruit in fruits:
print(f"I like {fruit}")
```
</div>
<div data-step="3" data-animation="slide-in-right">
```python
# Loop through numbers
for i in range(5):
print(f"Count: {i}") # 0, 1, 2, 3, 4
```
</div>
### While Loops
<div data-step="4" data-animation="scale-in">### While Loops</div>
<div data-step="5" data-animation="slide-in-up">
```python
count = 0
while count < 3:
print(f"Count is {count}")
count += 1 # Same as count = count + 1
```
</div>
</Slide>
<Slide>
## Functions - Organizing Your Code
### Creating Functions
<div data-step="1" data-animation="fade-in">### Creating Functions</div>
<div data-step="2" data-animation="type-in">
```python
def greet(name):
"""This function greets someone"""
return f"Hello, {name}!"
```
</div>
<div data-step="3" data-animation="slide-in-up">
```python
def add_numbers(a, b):
"""Add two numbers and return the result"""
result = a + b
return result
```
</div>
# Using functions
<div data-step="4" data-animation="bounce-in">### Using functions</div>
<div data-step="5" data-animation="slide-in-up">
```python
message = greet("Alice")
print(message) # Hello, Alice!
sum_result = add_numbers(5, 3)
print(sum_result) # 8
```
</div>
</Slide>
<Slide>
## Practice Exercise
### Build a Simple Calculator
<div data-step="1" data-animation="fade-in">### Build a Simple Calculator</div>
<div data-step="2" data-animation="type-in">
```python
def calculator():
print("Simple Calculator")
@@ -231,7 +297,11 @@ def calculator():
num1 = float(input("Enter first number: "))
operation = input("Enter operation (+, -, *, /): ")
num2 = float(input("Enter second number: "))
```
</div>
<div data-step="3" data-animation="slide-in-up">
```python
if operation == "+":
result = num1 + num2
elif operation == "-":
@@ -247,24 +317,29 @@ def calculator():
return "Error: Invalid operation!"
return f"Result: {result}"
```
</div>
<div data-step="4" data-animation="bounce-in">
```python
# Run the calculator
print(calculator())
```
</div>
</Slide>
<Slide >
## 🎉 Congratulations!
You've learned the basics of Python programming!
<div data-step="1" data-animation="scale-in">You've learned the basics of Python programming!</div>
### What's Next?
- Practice with small projects
- Learn about modules and packages
- Explore Python libraries
- Build something cool!
<div data-step="2" data-animation="fade-in">### What's Next?</div>
<div data-step="3" data-animation="slide-in-left">- Practice with small projects</div>
<div data-step="4" data-animation="slide-in-left">- Learn about modules and packages</div>
<div data-step="5" data-animation="slide-in-left">- Explore Python libraries</div>
<div data-step="6" data-animation="slide-in-left">- Build something cool!</div>
**Ready to code? Let's build something amazing! 🚀**
<div data-step="7" data-animation="bounce-in">**Ready to code? Let's build something amazing! 🚀**</div>
</Slide>
## Course Materials
@@ -291,4 +366,4 @@ By the end of this presentation, you'll be able to:
- Create and use functions
- Build a simple calculator program
The presentation automatically starts when you visit this page. Use arrow keys to navigate between slides, or escape to exit presentati
The presentation automatically starts when you visit this page. Use arrow keys to navigate between slides and steps, or escape to exit presentation mode.