Compare commits

...

5 Commits

3 changed files with 150 additions and 537 deletions

View File

@ -1,16 +1,15 @@
<template> <template>
<div> <div>
<div class="grid-container"> <div class="grid-container">
<!-- Linker Text --> <!-- Linker Text mit roten Dots -->
<div v-if="!hideText" class="item1"> <div v-if="!hideText" class="item1">
<div class="text-pair">anspruchsvoll</div> <div v-for="(label, index) in leftLabels" :key="index" class="text-pair">
<div class="text-pair">lustig</div> <span v-if="sliders[index].clicked" class="dot" />
<div class="text-pair">spannend</div> {{ label }}
<div class="text-pair">brutal</div> </div>
<div class="text-pair">sittsam</div>
</div> </div>
<!-- Fieberkurve und Punkte --> <!-- Fieberkurve (Slider & Punkte) -->
<div class="item2"> <div class="item2">
<div class="FieberkurveGesamt"> <div class="FieberkurveGesamt">
<div <div
@ -19,7 +18,12 @@
class="Fieberkurve" class="Fieberkurve"
:class="{ active: slider.clicked }" :class="{ active: slider.clicked }"
> >
<div v-for="i in 7" :key="i" class="kreis" /> <div
v-for="i in 7"
:key="i"
class="kreis"
:class="{ 'kreis-active': slider.clicked }"
/>
<input <input
v-model="slider.value" v-model="slider.value"
type="range" type="range"
@ -42,6 +46,7 @@
/> />
</div> </div>
<!-- Linie -->
<svg class="line-svg"> <svg class="line-svg">
<polyline <polyline
:points="linePoints" :points="linePoints"
@ -55,11 +60,9 @@
<!-- Rechter Text --> <!-- Rechter Text -->
<div v-if="!hideText" class="item3"> <div v-if="!hideText" class="item3">
<div class="text-pair">leseleicht</div> <div v-for="label in rightLabels" :key="label" class="text-pair">
<div class="text-pair">traurig</div> {{ label }}
<div class="text-pair">entspannend</div> </div>
<div class="text-pair">gewaltfrei</div>
<div class="text-pair">erotisch</div>
</div> </div>
</div> </div>
@ -78,11 +81,11 @@ const props = defineProps({
defaultValues: { defaultValues: {
type: Array, type: Array,
default: () => [ default: () => [
{ value: 4, clicked: false }, { value: 4, clicked: false, lastClickedValue: null },
{ value: 4, clicked: false }, { value: 4, clicked: false, lastClickedValue: null },
{ value: 4, clicked: false }, { value: 4, clicked: false, lastClickedValue: null },
{ value: 4, clicked: false }, { value: 4, clicked: false, lastClickedValue: null },
{ value: 4, clicked: false }, { value: 4, clicked: false, lastClickedValue: null },
], ],
}, },
hideText: { type: Boolean, default: false }, hideText: { type: Boolean, default: false },
@ -93,6 +96,9 @@ defineExpose({ resetSliders })
const sliders = ref([...props.defaultValues]) const sliders = ref([...props.defaultValues])
const leftLabels = ["anspruchsvoll", "lustig", "spannend", "brutal", "sittsam"]
const rightLabels = ["leseleicht", "traurig", "entspannend", "gewaltfrei", "erotisch"]
const linePoints = computed(() => { const linePoints = computed(() => {
const Breite = 240 const Breite = 240
const Hoehe = 130 const Hoehe = 130
@ -123,7 +129,18 @@ watch(
) )
function handleClick(index) { function handleClick(index) {
sliders.value[index].clicked = true const clickedSlider = sliders.value[index]
const currentValue = Number(clickedSlider.value)
if (clickedSlider.clicked && clickedSlider.lastClickedValue === currentValue) {
// Deaktivieren
clickedSlider.clicked = false
clickedSlider.value = 4
clickedSlider.lastClickedValue = null
} else {
clickedSlider.clicked = true
clickedSlider.lastClickedValue = currentValue
}
const activeFilters = sliders.value const activeFilters = sliders.value
.map((slider, idx) => .map((slider, idx) =>
@ -138,17 +155,18 @@ function resetSliders() {
sliders.value.forEach(slider => { sliders.value.forEach(slider => {
slider.value = 4 slider.value = 4
slider.clicked = false slider.clicked = false
slider.lastClickedValue = null
}) })
emit("filter-by-row", []) emit("filter-by-row", [])
} }
function updateLine() { function updateLine() {
// Linien-Update falls nötig // Optional: Linienanimation
} }
onMounted(() => { onMounted(() => {
// Initialisierung falls nötig // Init
}) })
</script> </script>
@ -170,6 +188,29 @@ onMounted(() => {
display: flex; display: flex;
align-items: center; align-items: center;
height: 26px; height: 26px;
font-size: 14px;
}
/* 🔴 Dot neben Text */
.dot {
width: 8px;
height: 8px;
background-color: #f45246;
border-radius: 50%;
display: inline-block;
margin-right: 6px;
animation: pop 0.3s ease;
}
@keyframes pop {
from {
transform: scale(0);
opacity: 0;
}
to {
transform: scale(1);
opacity: 1;
}
} }
.FieberkurveGesamt { .FieberkurveGesamt {
@ -189,10 +230,6 @@ onMounted(() => {
background-color: transparent; background-color: transparent;
} }
.Fieberkurve.active {
background-color: transparent;
}
.kreis { .kreis {
z-index: 2; z-index: 2;
width: 20px; width: 20px;
@ -200,6 +237,24 @@ onMounted(() => {
border-radius: 100%; border-radius: 100%;
background: #ceccccdb; background: #ceccccdb;
box-shadow: 2px 12px 27px -13px rgba(0, 0, 0, 0.48); box-shadow: 2px 12px 27px -13px rgba(0, 0, 0, 0.48);
transition: transform 0.2s ease;
}
/* 🔁 Wackel-Animation */
.kreis-active {
animation: wobble 0.3s ease;
}
@keyframes wobble {
0% {
transform: scale(1);
}
50% {
transform: scale(1.25);
}
100% {
transform: scale(1);
}
} }
.slider-bg { .slider-bg {
@ -273,489 +328,3 @@ onMounted(() => {
margin-top: 10px; margin-top: 10px;
} }
</style> </style>
<!-- Alter Code Michi von Fieberkurve 19.01.2025 -->
<!--
<script setup>
import { ref, watch, defineProps, defineEmits } from "vue"
const props = defineProps({
showResetButton: {
type: Boolean,
required: true,
},
showCategories: {
type: Boolean,
required: true,
},
})
/*
const emit = defineEmits(['buttonClicked'])
const emitButtonClick = () => {
emit('buttonClicked', 1)
}
*/
const schwierigkeit = ref(4)
const stimmung = ref(4)
const spannung = ref(4)
const gewalt = ref(4)
const erotik = ref(4)
const ColorRow1 = ref('#a8a6a6')
const ColorRow2 = ref('#a8a6a6')
const ColorRow3 = ref('#a8a6a6')
const ColorRow4 = ref('#a8a6a6')
const ColorRow5 = ref('#a8a6a6')
const isRow1Active = ref(false)
const isRow2Active = ref(false)
const isRow3Active = ref(false)
const isRow4Active = ref(false)
const isRow5Active = ref(false)
const isResetButtonPressed = ref(false)
const firstClick = ref(false)
function FevercurveIsClicked(RowValue) {
isResetButtonPressed.value = true
switch (RowValue) {
case "schwierigkeit":
console.log("Line: 1")
ColorRow1.value = '#cccccc'
isRow1Active.value = true
break
case "stimmung":
console.log("Line: 2")
ColorRow2.value = '#cccccc'
isRow2Active.value = true
break
case "spannung":
console.log("Line: 3")
ColorRow3.value = '#cccccc'
isRow3Active.value = true
break
case "gewalt":
console.log("Line: 4")
ColorRow4.value = '#cccccc'
isRow4Active.value = true
break
case "erotik":
console.log("Line: 5")
ColorRow5.value = '#cccccc'
isRow5Active.value = true
break
}
console.log("firstClick:", firstClick.value)
if (!firstClick.value) {
rowFevercurveConnectors()
}
firstClick.value = true
}
function resetFevercurve() {
if (!isResetButtonPressed.value) {
return
}
console.log("reset Fevercurve")
isResetButtonPressed.value = false
firstClick.value = false
ColorRow1.value = '#a8a6a6'
ColorRow2.value = '#a8a6a6'
ColorRow3.value = '#a8a6a6'
ColorRow4.value = '#a8a6a6'
ColorRow5.value = '#a8a6a6'
isRow1Active.value = false
isRow2Active.value = false
isRow3Active.value = false
isRow4Active.value = false
isRow5Active.value = false
schwierigkeit.value = 4
stimmung.value = 4
spannung.value = 4
gewalt.value = 4
erotik.value = 4
clearFevercurveConnectors()
}
function clearFevercurveConnectors() {
console.log("Striche werden gecleart")
let c = document.getElementById("myCanvas")
let ctx = c.getContext("2d")
ctx.clearRect(0, 0, c.width, c.height)
}
function rowFevercurveConnectors() {
console.log("Funktion wird aufgerufen")
let c = document.getElementById("myCanvas")
let ctx = c.getContext("2d")
//settings
// let gradient = ctx.createLinearGradient(182, 5, 41, 100)
// gradient.addColorStop("0", "#3f5efb")
// gradient.addColorStop("1.0", "#b60529")
// ctx.strokeStyle = gradient
ctx.strokeStyle = "red"
ctx.lineWidth = 4
// clear canvas
ctx.clearRect(0, 0, c.width, c.height)
ctx.beginPath()
let Breite = 240
let Hoehe = 130
let Kreis = 20
ctx.moveTo(((schwierigkeit.value - 1) * ((Breite - Kreis) / 6) + (Kreis / 2)), ((1 - 1) * ((Hoehe - Kreis) / 4) + (Kreis / 2)))
ctx.lineTo(((stimmung.value - 1) * ((Breite - Kreis) / 6) + (Kreis / 2)), ((2 - 1) * ((Hoehe - Kreis) / 4) + (Kreis / 2)))
ctx.lineTo(((spannung.value - 1) * ((Breite - Kreis) / 6) + (Kreis / 2)), ((3 - 1) * ((Hoehe - Kreis) / 4) + (Kreis / 2)))
ctx.lineTo(((gewalt.value - 1) * ((Breite - Kreis) / 6) + (Kreis / 2)), ((4 - 1) * ((Hoehe - Kreis) / 4) + (Kreis / 2)))
ctx.lineTo(((erotik.value - 1) * ((Breite - Kreis) / 6) + (Kreis / 2)), ((5 - 1) * ((Hoehe - Kreis) / 4) + (Kreis / 2)))
ctx.stroke()
}
watch([schwierigkeit, stimmung, spannung, gewalt, erotik], () => {
if (isResetButtonPressed.value){
rowFevercurveConnectors()
}
})
</script>
<template>
<div>
<div class="grid-container">
<div
v-if="props.showCategories"
class="item1"
>
anspruchsvoll <br>
lustig <br>
spannend <br>
brutal <br>
sittsam
</div>
<div class="item2">
&lt;!&ndash; Slider &ndash;&gt;
<div style="position: relative; height: 120px">
&lt;!&ndash; Fieberkurve &ndash;&gt;
<canvas
id="myCanvas"
width="240"
height="130"
>
Your browser does not support the HTML canvas tag.
</canvas>
<div class="FieberkurveGesamt">
<div
class="Fieberkurve"
@click="FevercurveIsClicked('schwierigkeit')"
>
<input
id="myRange"
v-model="schwierigkeit"
type="range"
min="1"
max="7"
class="slider"
:style="{ color: '#000000' }"
>
<div
class="slider-track"
:style="{ backgroundColor: ColorRow1 }"
/>
<div class="kreis" />
<div class="kreis" />
<div class="kreis" />
<div class="kreis" />
<div class="kreis" />
<div class="kreis" />
<div class="kreis" />
</div>
&lt;!&ndash; :style="{ backgroundColor: vuetifyTheme.current.value.colors.primary }" &ndash;&gt;
<div
class="Fieberkurve"
@click="FevercurveIsClicked('stimmung')"
>
<input
id="myRange"
v-model="stimmung"
type="range"
min="1"
max="7"
class="slider"
>
<div
class="slider-track"
:style="{ backgroundColor: ColorRow2 }"
/>
<div class="kreis" />
<div class="kreis" />
<div class="kreis" />
<div class="kreis" />
<div class="kreis" />
<div class="kreis" />
<div class="kreis" />
</div>
<div
class="Fieberkurve"
@click="FevercurveIsClicked('spannung')"
>
<input
id="myRange"
v-model="spannung"
type="range"
min="1"
max="7"
class="slider"
>
<div
class="slider-track"
:style="{ backgroundColor: ColorRow3 }"
/>
<div class="kreis" />
<div class="kreis" />
<div class="kreis" />
<div class="kreis" />
<div class="kreis" />
<div class="kreis" />
<div class="kreis" />
</div>
<div
class="Fieberkurve"
@click="FevercurveIsClicked('gewalt')"
>
<input
id="myRange"
v-model="gewalt"
type="range"
min="1"
max="7"
class="slider"
>
<div
class="slider-track"
:style="{ backgroundColor: ColorRow4 }"
/>
<div class="kreis" />
<div class="kreis" />
<div class="kreis" />
<div class="kreis" />
<div class="kreis" />
<div class="kreis" />
<div class="kreis" />
</div>
<div
class="Fieberkurve"
@click="FevercurveIsClicked('erotik')"
>
<input
id="myRange5"
v-model="erotik"
type="range"
min="1"
max="7"
class="slider"
:class="{ backgroundColor: '#e70707' }"
>
<div
class="slider-track"
:style="{ backgroundColor: ColorRow5 }"
/>
<div class="kreis" />
<div class="kreis" />
<div class="kreis" />
<div class="kreis" />
<div class="kreis" />
<div class="kreis" />
<div class="kreis" />
</div>
</div>
</div>
</div>
<div
v-if="props.showCategories"
class="item3"
>
leseleicht<br>
traurig<br>
entspannend<br>
gewaltfrei<br>
erotisch
</div>
</div>
&lt;!&ndash; ENDE FIEBERKURVE &ndash;&gt;
&lt;!&ndash; Button Fieberkurve zurücksetzen &ndash;&gt;
<div
v-if="props.showResetButton"
style="
width: 100%;
margin: 5px;
display: flex;
justify-content: center;"
>
<VBtn
size="small"
@click="resetFevercurve"
>
Fieberkurve zurücksetzen
</VBtn>
</div>
</div>
</template>
<style scoped lang="scss">
//Grid Layout
.item1 {
grid-area: txtLinks;
background: transparent !important;
}
.item2 {
grid-area: Fieberkurve;
background: transparent !important;
}
.item3 {
grid-area: txtRechts;
background: transparent !important;
}
.grid-container {
display: grid;
grid-template-areas: 'txtLinks Fieberkurve txtRechts';
gap: 10px;
justify-content: center;
line-height: 26px;
}
.grid-container > div {
background-color: rgba(255, 255, 255, 0.8);
}
//Grid Layout END
canvas {
z-index: 2;
//border:1px solid #000000;
position: absolute;
}
.FieberkurveGesamt {
height: 130px;
width: 240px;
position: relative;
display: flex;
flex-wrap: wrap;
align-content: space-between;
}
.Fieberkurve {
position: relative;
width: 240px;
display: flex;
justify-content: space-between;
//background: #27c21f;
//padding: ;
border-radius: 25px;
}
.kreis {
z-index: 1;
width: 20px;
height: 20px;
border-radius: 100%;
background: #CECCCCDB;
box-shadow: 2px 12px 27px -13px rgba(0,0,0,0.48);
}
.slider {
z-index: 3;
width: 100%;
position: absolute;
//top: 10px;
-webkit-appearance: none;
//background: #c29c1f;
//height: 7.5px;
//margin-top: 3.5px;
//background: rgba(50, 150, 11, 0.81);
outline: none;
//border: 5px solid rgba(232, 64, 64, 0.83);
border-radius: 8px;
cursor: pointer;
}
.slider-track {
position: absolute;
width: 100%;
border-radius: 8px;
//background: #bb2626;
height: 10px;
margin-top: 5px;
}
/* Kreis */
/* for chrome/safari */
.slider::-webkit-slider-thumb {
//z-index: 4;
-webkit-appearance: none;
appearance: none;
width: 20px;
height: 20px;
background: #F07D00;
cursor: pointer;
//border: 5px solid red;
border-radius: 100%;
}
/* for firefox */
.slider::-moz-range-thumb {
width: 20px;
height: 20px;
background: #000;
cursor: pointer;
//border: 5px solid lawngreen;
border-radius: 4px;
}
</style>
-->

View File

@ -95,7 +95,7 @@ export const books = [
pageCount: 694, pageCount: 694,
language: "de", language: "de",
publishedDate: "1996-08", publishedDate: "1996-08",
image: "https://m.media-amazon.com/images/I/91L2Dhn2xBL._AC_UF1000,1000_QL80_.jpg", image: "https://m.media-amazon.com/images/I/81YOuOGFCJL.jpg",
fieberkurve: [ fieberkurve: [
{ value: 5 }, { value: 5 },
{ value: 2 }, { value: 2 },
@ -137,7 +137,7 @@ export const books = [
pageCount: 454, pageCount: 454,
language: "de", language: "de",
publishedDate: "2003-03", publishedDate: "2003-03",
image: "https://m.media-amazon.com/images/I/81Q9NhedqgL.jpg", image: "https://m.media-amazon.com/images/I/81YOuOGFCJL.jpg",
fieberkurve: [ fieberkurve: [
{ value: 1 }, { value: 1 },
{ value: 7 }, { value: 7 },

View File

@ -17,6 +17,7 @@
<br> <br>
<!-- Genres --> <!-- Genres -->
<div class="genre-section-main"> <div class="genre-section-main">
<div <div
@ -74,35 +75,76 @@
</VCard> </VCard>
</VCol> </VCol>
<!-- Gefundene Bücher -->
<VCol cols="12" md="12"> <VCol cols="12" md="12">
<VCard class="mb-6" title="Gefundene Bücher:"> <VCard class="mb-6">
<!-- Trefferanzahl -->
<VCardTitle class="text-h5 px-6 pt-4 pb-2">
{{ resultCount }} {{ resultCount === 1 ? 'Buch' : 'Bücher' }} gefunden
</VCardTitle>
<VDivider class="my-2" />
<VRow> <VRow>
<VCol v-for="book in filteredBooks" :key="book.id" sm="6" cols="12"> <VCol v-for="book in filteredBooks" :key="book.id" cols="12" sm="6" md="4">
<VCard> <VCard class="pa-2">
<div class="d-flex justify-space-between flex-wrap flex-md-nowrap flex-column flex-md-row"> <VCardText class="d-flex flex-column align-center">
<div class="ma-auto pa-5"> <!-- Bild -->
<VImg width="200px" height="300px" :src="book.image" /> <VImg
:src="book.image"
width="160"
height="240"
class="mb-3 rounded"
cover
/>
<!-- Titel -->
<div class="text-h6 font-weight-bold text-center">
{{ book.title }}
</div> </div>
<VDivider :vertical="$vuetify.display.mdAndUp" /> <!-- Autoren -->
<div v-if="book.authors?.length" class="text-caption mb-2 text-center">
<div> von {{ book.authors.map(a => a.name).join(', ') }}
<VCardTitle>{{ book.title }}</VCardTitle>
<VCardText>{{ book.description }}</VCardText>
<Fieberkurve
:is-static="true"
:default-values="book.fieberkurve"
:hide-text="true"
/>
<VCardActions class="justify-space-between">
<VBtn>
<VIcon icon="tabler-list-details" />
<span class="ms-2">Detail Seite</span>
</VBtn>
<IconBtn color="secondary" icon="tabler-share" />
</VCardActions>
</div> </div>
</div>
<!-- Genres -->
<div class="d-flex flex-wrap justify-center mb-2">
<VChip
v-for="genre in book.genres"
:key="genre"
color="primary"
size="small"
class="ma-1"
>
{{ genre }}
</VChip>
</div>
<!-- Beschreibung (Blurb) -->
<div
class="text-truncate mb-3 text-center"
style="max-width: 90%; max-height: 3em; overflow: hidden;"
>
{{ book.blurb }}
</div>
<!-- Fieberkurve -->
<Fieberkurve
:is-static="true"
:default-values="book.fieberkurve"
:hide-text="true"
/>
<!-- Aktionen -->
<VCardActions class="justify-center mt-2">
<VBtn size="small" variant="outlined">
<VIcon icon="tabler-list-details" />
<span class="ms-2">Detail Seite</span>
</VBtn>
<IconBtn color="secondary" icon="tabler-share" />
</VCardActions>
</VCardText>
</VCard> </VCard>
</VCol> </VCol>
</VRow> </VRow>
@ -123,6 +165,8 @@ const selectedCheckbox = ref([]);
const fieberkurveValues = ref([4, 4, 4, 4, 4]); const fieberkurveValues = ref([4, 4, 4, 4, 4]);
const fieberkurveRowFilter = ref([]); const fieberkurveRowFilter = ref([]);
const resultCount = computed(() => filteredBooks.value.length)
const filteredBooks = computed(() => { const filteredBooks = computed(() => {
return books.filter((book) => { return books.filter((book) => {
const matchesGenres = const matchesGenres =