Files for my website bwc9876.dev
at main 139 lines 2.8 kB view raw
1--- 2import { Image } from "astro:assets"; 3import backdrop from "@assets/backdrop.webp"; 4import type { CollectionEntry } from "astro:content"; 5 6export interface Props { 7 project: CollectionEntry<"projects">; 8} 9 10const stringToNumber = (str: string, seed = 3) => { 11 let h1 = 0xdeadbeef ^ seed, 12 h2 = 0x41c6ce57 ^ seed; 13 for (let i = 0, ch; i < str.length; i++) { 14 ch = str.charCodeAt(i); 15 h1 = Math.imul(h1 ^ ch, 2654435761); 16 h2 = Math.imul(h2 ^ ch, 1597334677); 17 } 18 h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507) ^ Math.imul(h2 ^ (h2 >>> 13), 3266489909); 19 h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507) ^ Math.imul(h1 ^ (h1 >>> 13), 3266489909); 20 return 4294967296 * (2097151 & h2) + (h1 >>> 0); 21}; 22 23const getHueFromText = (text: string): string => `hue-rotate(${stringToNumber(text) % 360}deg)`; 24 25const { project } = Astro.props; 26--- 27 28<a href={`/projects/${project.id}/`}> 29 <article> 30 <div> 31 <Image 32 class="thumbnail" 33 transition:name={`project-img-${project.id}`} 34 src={project.data.image} 35 densities={[1, 1.5, 2]} 36 width="400" 37 alt="" 38 /> 39 <Image 40 style={{ filter: getHueFromText(project.id) }} 41 class="backdrop" 42 src={backdrop} 43 alt="Backdrop" 44 /> 45 </div> 46 <div> 47 <h3 class="project-name">{project.data.name}</h3> 48 <small>{project.data.tags.join(", ")}</small> 49 <p>{project.data.summary}</p> 50 </div> 51 </article> 52</a> 53 54<style> 55 :not(.project-name) { 56 text-decoration: none !important; 57 } 58 59 small { 60 margin: 0 var(--1); 61 display: block; 62 text-wrap: balance; 63 } 64 65 h3 { 66 margin: var(--1) var(--1) 0 var(--1); 67 } 68 69 p { 70 margin: var(--1); 71 } 72 73 article { 74 display: flex; 75 flex-direction: column; 76 height: 100%; 77 color: var(--text); 78 background-color: var(--secondary); 79 border-radius: 5px; 80 } 81 82 a { 83 transition: transform cubic-bezier(0.68, -0.55, 0.27, 1.55) 0.4s; 84 } 85 86 a:hover { 87 transform: translateY(calc(var(--1) * -1)); 88 } 89 90 a:hover img { 91 transform: scale(1.06); 92 } 93 94 span { 95 flex-grow: 1; 96 text-align: center; 97 display: flex; 98 align-items: center; 99 justify-content: center; 100 } 101 102 div:first-child { 103 display: flex; 104 flex-direction: column; 105 align-items: center; 106 } 107 108 div { 109 overflow: hidden; 110 position: relative; 111 } 112 113 img, 114 div:first-child { 115 border-top-left-radius: 5px; 116 border-top-right-radius: 5px; 117 } 118 119 img { 120 position: relative; 121 object-fit: contain; 122 width: 100%; 123 height: auto; 124 transition: transform ease-in-out 0.4s; 125 height: var(--14); 126 } 127 128 img.thumbnail { 129 z-index: 2; 130 } 131 132 img.backdrop { 133 position: absolute; 134 transform: scale(1.1); 135 inset: 0; 136 object-fit: cover; 137 filter: blur(6px); 138 } 139</style>