use component variable in scss style - vue.js

I have this kenburns gallery slideshow which I did statically and I would love to make it more dynamic now ...
so I have this component:
<template>
<div>
<div class="slideshow">
<v-img v-for="photo in photos" :key="photo.id_photo" :src="photo.full_img_path" class="slideshow-image"></v-img>
</div>
<Header></Header>
</div>
</template>
<script>
import Header from './shared/Header.vue';
import PublicService from './public.service';
export default {
name: 'Home',
components: {
Header
},
data: () => ({
serverHost: process.env.VUE_APP_API_BASE_URL,
photos: []
}),
created() {
this.getHomePagePhotos();
},
computed: {},
methods: {
getHomePagePhotos() {
PublicService.getHomePagePhotos().then(
result => {
this.photos = result?.data;
this.photos.forEach(p => {
p.full_img_path = this.serverHost + p.photo_path.replace('/uploads', '') + '/' + p.photo_name;
});
},
error => {
console.log(error);
}
);
}
}
};
</script>
<style lang="scss">
$items: 4;
$animation-time: 4s;
$transition-time: 0.5s;
$scale: 20%;
$total-time: ($animation-time * $items);
$scale-base-1: (1 + $scale / 100%);
.slideshow {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
.slideshow-image {
position: absolute;
width: 100%;
height: 100%;
background: no-repeat 50% 50%;
background-size: cover;
animation-name: kenburns;
animation-timing-function: linear;
animation-iteration-count: infinite;
animation-duration: $total-time;
opacity: 1;
transform: scale($scale-base-1) translateX(500px);
#for $i from 1 through $items {
&:nth-child(#{$i}) {
animation-name: kenburns-#{$i};
z-index: ($items - $i);
}
}
}
#for $i from 1 through $items {
#keyframes kenburns-#{$i} {
$animation-time-percent: percentage($animation-time / $total-time);
$transition-time-percent: percentage($transition-time / $total-time);
$t1: ($animation-time-percent * ($i - 1) - $transition-time-percent / 2);
$t2: ($animation-time-percent * ($i - 1) + $transition-time-percent / 2);
#if ($t1 < 0%) {
$t1: 0%;
}
#if ($t2 < 0%) {
$t2: 0%;
}
$t3: ($animation-time-percent * ($i) - $transition-time-percent / 2);
$t4: ($animation-time-percent * ($i) + $transition-time-percent / 2);
#if ($t3 > 100%) {
$t3: 100%;
}
#if ($t4 > 100%) {
$t4: 100%;
}
$t5: (100% - $transition-time-percent / 2);
$t6: (($t4 - $t1) * 100% / $t5);
#{$t1} {
opacity: 1;
transform: scale($scale-base-1) translateX(($i * 72px % 200px)-100px);
}
#{$t2} {
opacity: 1;
}
#{$t3} {
opacity: 1;
}
#{$t4} {
opacity: 0;
transform: scale(1);
}
#if ($i != $items) {
100% {
opacity: 0;
transform: scale($scale-base-1);
}
}
#if ($i == 1) {
$scale-plus: ($scale * (100% - $t5) / $t4);
$scale-plus-base-1: (1 + ($scale + $scale-plus) / 100%);
#{$t5} {
opacity: 0;
transform: scale($scale-plus-base-1);
}
100% {
opacity: 1;
}
}
}
}
</style>
And I wonder how do replace the very first scss variable
$items: 4;
with the actual number of items coming from the server
$items: this.photos.length;
I googled a lot but didn't find anything .. any idea?

As others have said I don't believe it's possible to set SASS variables in Vue 2. You can set CSS variables however I'm not sure if it's possible to implement those into what you're trying to achieve
https://shayneo.com/blog/binding-css-variables-with-vue/

Related

Using wheelnav.js on Vue 3 project

So I have seen this pie menu generator which gives you an HTML, CSS and JS code and I wanted to use it in my Vue 3 project. I am new to vue and this was how I imported it.
here is the link to the pie menu generator: http://pmg.softwaretailoring.net/
I did this in my wrapper component. Somehow this bought many errors in which I think is because of how I imported the JS library.
<template>
<div class="context-menu" v-show="show" :style="style" ref="context" tabindex="0" #blur="close">
<div id='piemenu' data-wheelnav data-wheelnav-slicepath='DonutSlice' data-wheelnav-marker
data-wheelnav-markerpath='PieLineMarker' data-wheelnav-rotateoff data-wheelnav-navangle='270'
data-wheelnav-cssmode data-wheelnav-init>
<div data-wheelnav-navitemtext='0' onmouseup='alert("Place your logic here.");'></div>
<div data-wheelnav-navitemtext='1' onmouseup='alert("Place your logic here.");'></div>
</div>
</div>
</template>
<script>
import '../assets/raphael.min.js'
import '../assets/raphael.icons.min.js'
import '../assets/wheelnav.min.js'
var piemenu = new wheelnav('piemenu');
piemenu.wheelRadius = piemenu.wheelRadius * 0.83;
piemenu.createWheel();
export default {
name: 'CmpContextMenu',
props: {
display: Boolean, // prop detect if we should show context menu
},
data() {
return {
left: 0, // left position
top: 0, // top position
show: false, // affect display of context menu
};
},
computed: {
// get position of context menu
style() {
return {
top: this.top + 'px',
left: this.left + 'px',
};
},
},
methods: {
// closes context menu
close() {
this.show = false;
this.left = 0;
this.top = 0;
this.myTrigger = false;
console.log('trigger false');
},
open(evt) {
this.show = true;
// updates position of context menu
this.left = evt.pageX || evt.clientX;
this.top = evt.pageY || evt.clientY;
},
},
};
</script>
<style>
.context-menu {
position: fixed;
z-index: 999;
cursor: pointer;
}
#piemenu>svg {
width: 100%;
height: 100%;
}
#piemenu {
height: 400px;
width: 400px;
margin: auto;
}
#media (max-width: 400px) {
#piemenu {
height: 300px;
width: 300px;
}
}
[class|=wheelnav-piemenu-slice-basic] {
fill: #497F4C;
stroke: none;
}
[class|=wheelnav-piemenu-slice-selected] {
fill: #497F4C;
stroke: none;
}
[class|=wheelnav-piemenu-slice-hover] {
fill: #497F4C;
stroke: none;
fill-opacity: 0.77;
cursor: pointer;
}
[class|=wheelnav-piemenu-title-basic] {
fill: #333;
stroke: none;
}
[class|=wheelnav-piemenu-title-selected] {
fill: #fff;
stroke: none;
}
[class|=wheelnav-piemenu-title-hover] {
fill: #222;
stroke: none;
cursor: pointer;
}
[class|=wheelnav-piemenu-title]>tspan {
font-family: Impact, Charcoal, sans-serif;
font-size: 24px;
}
.wheelnav-piemenu-marker {
stroke: #444;
stroke-width: 2;
}
</style>

Reinitialize matterjs after page changes in nuxtjs

I have a matterjs instance in my nuxt app that drops items on the floor. Everything works when I visit the page for the first time or do a page refresh. But when I change the pages (routes) inside my app, so I come back to the page with the matterjs instance, the instance is gone. I always have to do a page refresh...
How can I reinitialize matterjs?
Fallbox
<section class="fallbox">
<div class="fallbox-content">
<nuxt-link to="/"><h1>Index</h1></nuxt-link>
</div>
<div class="fallbox-scene">
<div v-for="item in items" :key="item.className">
<span :class="item.className" class="item"></span>
</div>
</div>
</section>
export default {
data() {
return {
items: [
{
className: "-i1",
},
{
className: "-i2",
},
{
className: "-i3",
},
{
className: "-i4",
},
{
className: "-i5",
},
],
};
},
mounted() {
window.addEventListener("DOMContentLoaded", () => {
this.startFallbox();
});
},
methods: {
startFallbox() {
const Engine = Matter.Engine;
const Render = Matter.Render;
const Runner = Matter.Runner;
const Bodies = Matter.Bodies;
const Body = Matter.Body;
const Composite = Matter.Composite;
const MouseConstraint = Matter.MouseConstraint;
const engine = Engine.create();
const world = engine.world;
engine.gravity.y = 1;
const fallbox = document.querySelector(".fallbox-scene");
const render = Render.create({
element: fallbox,
engine,
options: {
width: fallbox.offsetWidth,
height: fallbox.offsetHeight,
},
});
// Render.run(render);
const runner = Runner.create();
Runner.run(runner, engine);
const itemArray = this.items;
itemArray.forEach((i) => {
const get = document.getElementsByClassName(i.className)[0];
get.style.opacity = 1;
const item = {
w: get.clientWidth,
h: get.clientHeight,
body: Bodies.rectangle(
Math.random() * window.innerWidth,
Math.random() * -1000,
get.clientWidth,
get.clientHeight,
{
restitution: 0.5,
angle: Math.random() * 360,
}
),
elem: get,
render() {
const { x, y } = this.body.position;
this.elem.style.top = `${y - this.h / 2}px`;
this.elem.style.left = `${x - this.w / 2}px`;
this.elem.style.transform = `rotate(${this.body.angle}rad)`;
},
};
Body.rotate(item.body, Math.random() * 360);
Composite.add(world, [item.body]);
(function rerender() {
item.render();
requestAnimationFrame(rerender);
})();
});
const ground = Bodies.rectangle(
fallbox.offsetWidth / 2,
fallbox.offsetHeight,
2000,
1,
{
isStatic: true,
}
);
const left = Bodies.rectangle(
0,
fallbox.offsetHeight / 2,
1,
fallbox.offsetHeight,
{
isStatic: true,
}
);
const right = Bodies.rectangle(
fallbox.offsetWidth,
fallbox.offsetHeight / 2,
1,
fallbox.offsetHeight,
{
isStatic: true,
}
);
Composite.add(world, [ground, left, right]);
const mouseConstraint = MouseConstraint.create(engine, {
element: fallbox,
constraint: {
stiffness: 0.2,
},
});
mouseConstraint.mouse.element.removeEventListener(
"mousewheel",
mouseConstraint.mouse.mousewheel
);
mouseConstraint.mouse.element.removeEventListener(
"DOMMouseScroll",
mouseConstraint.mouse.mousewheel
);
Composite.add(world, mouseConstraint);
Render.lookAt(render, {
min: { x: 0, y: 0 },
max: { x: fallbox.offsetWidth, y: fallbox.offsetHeight },
});
},
},
};
.fallbox {
position: relative;
height: 100vh;
width: 100%;
margin: auto;
background: black;
overflow: hidden;
.fallbox-content {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 95%;
max-width: 1050px;
z-index: 2;
// -khtml-user-select: none;
// -moz-user-select: none;
// -ms-user-select: none;
// user-select: none;
// pointer-events: none;
h1 {
font-size: 160px;
font-weight: 500;
line-height: 140px;
margin-bottom: 150px;
text-align: center;
color: white;
}
}
.fallbox-scene {
height: 100%;
width: 100%;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 1;
contain: strict;
.item {
height: 120px;
width: 120px;
background: red;
position: absolute;
opacity: 0;
user-select: none;
will-change: transform;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
pointer-events: none;
}
}
}
activated() {
this.startFallbox()
}
Use the activated hook to restart the animation. The code when mounted() is still needed.
I assume the problem is that when you navigate away and you some back the initial animation is finished, but as Nuxt caches some pages they are served from cache and therefore the mounted() hook is not called. This is where the activated() hooks comes in. It's called when a page/component was keept alive and is reactivated.

How to pass dynamic content to Vue v-bind:style on #mousemove method

<div
class="BgContainer"
#mousemove="mouseMove"
v-bind:style="{
transform: 'matrix3d(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1)',
}"
>
I am trying to update a transform: matrix3d() style on mouseMove function
methods: {
mouseMove(event) {
console.log(event.clientX, event.clientY)
},
},
trying to learn Vue (Nuxt.js) and I am wondering what would be the best way to update this transform based on mouse position and update the transform on mouseMove. How can I achieve something like this where newX and newY are dynamic based on mouse position?
v-bind:style="{
transform: 'matrix3d(1.025,0,0,"newX",0,1.025,0,"newY",0,0,1,0,0,0,0,1)',
}"
EDIT - whole vue file
<template>
<div class="prop_scroller">
<div
class="BgContainer"
#mousemove="mouseMove"
v-bind:style="{
transform:
'matrix3d(1.025,0,0,' +
newX+
',0,1.025,0,' +
newY +
',0,0,1,0,0,0,0,1)',
}"
>
<i class="sacbg"> </i>
<div class="after"></div>
</div>
</div>
</template>
<script>
import VanillaTilt from 'vanilla-tilt'
export default {
data: function () {
newX: 0
newY: 0
},
mounted: function () {
// VanillaTilt.init(this.$refs.bg)
console.log(this.$refs)
},
methods: {
mouseMove(event) {
console.log(event.clientX, event.clientY)
},
},
}
</script>
<style lang="scss" scoped>
.prop_scroller {
position: absolute;
top: -50px;
right: -70px;
bottom: -60px;
left: -50px;
overflow: scroll;
}
.js-tilt-glare-inner {
backface-visibility: hidden;
}
.BgContainer {
height: 100%;
width: 100%;
overflow: hidden;
position: absolute;
// object-fit: cover;
z-index: 300;
backface-visibility: hidden;
transform-style: preserve-3d;
top: 0;
right: 0;
bottom: 0;
left: 0;
i {
position: absolute !important;
top: -70px !important;
right: -70px !important;
bottom: -60px !important;
left: -60px !important;
background-size: cover;
background-position: center;
background-image: url('~static/mural-bg.jpg');
}
.MuralBg {
// animation: 1s appear;
margin: auto;
width: 100vw;
height: auto;
}
.after {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(26, 33, 42, 0.2);
animation: 1.2s fadeBgOut;
}
}
#keyframes fadeBgOut {
0% {
background-color: rgba(26, 33, 42, 0.8);
}
100% {
background-color: rgba(26, 33, 42, 0.2);
}
}
</style>
Try out with string template literals :
v-bind:style="{
transform: `matrix3d(1.025,0,0,${newX},0,1.025,0,${newY},0,0,1,0,0,0,0,1)`,
}"
or by concatenation :
v-bind:style="{
transform: 'matrix3d(1.025,0,0,'+newX+',0,1.025,0,'+newY+',0,0,1,0,0,0,0,1)',
}"
and your data property should be a function that returns an object :
<script>
import VanillaTilt from 'vanilla-tilt'
export default {
data: function () {
return {
newX: 0
newY: 0
}
},
mounted: function () {
// VanillaTilt.init(this.$refs.bg)
console.log(this.$refs)
},
methods: {
mouseMove(event) {
console.log(event.clientX, event.clientY)
this.newX=event.clientX;
this.newY=event.clientY;
},
},
}
</script>

Vue 3 Composition API component doesn't work reactivate class

I have a button and I just want it animate on a click - make as it been pressed. It works if I make optional component as below:
<template>
<button class="button" :class="{'shadow__click': classButton}" #click="buttonClass">
Tell me already!
</button>
</template>
<script>
export default {
data(){
return {
classButton : false
}
},
methods: {
buttonClass(){
this.classButton = true;
setTimeout(() => {
this.classButton = false
}, 1000)
}
}
}
</script>
<style lang="less">
.button{
cursor: pointer;
padding: 12px 24px;
position: relative;
border-radius: 5px;
background-color: #38b2ac;
border: none;
color: #fff8e1;
}
.shadow__click{
animation: click 1s
}
#keyframes click {
0% {
top: 0;
left: 0;
}
25% {
top: 5px;
left: 1px;
}
50% {
top: 10px;
left: 3px;
}
75% {
top: 5px;
left: 1px;
}
100% {
top: 0;
left: 0;
}
}
</style>
but it doesn't want to work when I do Composition way and I don't see a problem but it simply doesn't work (( I console.loged function and it goes to function changes the value of a variable, but class is not applying. Is it a Vue 3 bug?
<script>
import { ref } from "vue"
setup(){
let classButton = ref(false);
function buttonClass(){
classButton = true;
setTimeout(() => {
classButton = false
}, 1000)
}
return { classButton, buttonClass}
}
</script>
You should mutate the ref using the value field :
let classButton = ref(false);
function buttonClass(){
classButton.value = true;
setTimeout(() => {
classButton.value = false
}, 1000)
}

Using svg-gauge in vue component

I'd like to use svg-gauge (https://github.com/naikus/svg-gauge) in a vue component (vue-svg-gauge does not work properly). I am struggling where to start. I have imported the library but am not sure about using it within vue. This is what I have done so far:
<script>
module.exports = {
props: {
'value': {type: Number, default: 20},
},
data: function () {return {
chart: null
}},
mounted: function () {
this.chart = new Gauge(
document.getElementById("gauge1"), {
min: 0,
max: 50,
dialStartAngle: 180,
dialEndAngle: 0,
value: this.value,
color: function(value) {
if (value < 0) {
return "#5ee432";
} else if(value < 25) {
return "#fffa50";
} else if(value < 50) {
return "#f7aa38";
} else {
return "#ef4655";
}
}
}
);
},
}
}
}
</script>
It renders a gauge but I'm not sure how to update it or if this is the right way of going aboout things?
Thanks
Martyn
Update:
I am now using High charts gauge as it see easier to configure.
The solution I came up with for the above question is as follows (from an old commit)
<template>
<div class="wrapper bg-dark text-light border-0">
<div id="gauge1" class="gauge-container two"></div>
</div>
</template>
<script>
module.exports = {
props: {
'value': {type: Number, default: 0},
},
data: function () {return {
chart: null
}},
mounted: function () {
this.chart = new Gauge(
document.getElementById("gauge1"), {
min: 0,
max: 50,
dialStartAngle: 180,
dialEndAngle: 0,
value: this.value,
color: function(value) {
if (value < 0) {
return "#5ee432";
} else if(value < 25) {
return "#fffa50";
} else if(value < 50) {
return "#f7aa38";
} else {
return "#ef4655";
}
}
}
);
},
beforeDestroy: function () {
// this.chart.destroy()
},
watch: {
value: function (val) {
this.chart.value = val
},
options: function (opt) {
// this.chart.destroy()
// this.chart = new RadialGauge(Object.assign({}, this.options, {renderTo: 'canvas-gauge', value: this.value})).draw()
}
}
}
</script>
<style scoped>
/* ------ Default Style ---------- */
.gauge-container {
width: 250px;
height: 250px;
display: block;
float: left;
padding: 10px;
background-color: #222;
margin: 7px;
border-radius: 3px;
position: relative;
}
.gauge-container > .label {
position: absolute;
right: 0;
top: 0;
display: inline-block;
background: rgba(0,0,0,0.5);
font-family: monospace;
font-size: 0.8em;
padding: 5px 10px;
}
.gauge-container > .gauge .dial {
stroke: #334455;
stroke-width: 2;
fill: rgba(0,0,0,0);
}
.gauge-container > .gauge .value {
stroke: rgb(47, 227, 255);
stroke-width: 2;
fill: rgba(0,0,0,0);
}
.gauge-container > .gauge .value-text {
fill: rgb(47, 227, 255);
font-family: sans-serif;
font-weight: bold;
font-size: 0.8em;
}
/* ------- Alternate Style ------- */
.wrapper {
height: 150px;
float: left;
margin: 7px;
overflow: hidden;
}
.wrapper > .gauge-container {
margin: 0;
}
.gauge-container.two {
}
.gauge-container.two > .gauge .dial {
stroke: #334455;
stroke-width: 10;
}
.gauge-container.two > .gauge .value {
stroke: orange;
stroke-dasharray: none;
stroke-width: 13;
}
.gauge-container.two > .gauge .value-text {
fill: #ccc;
font-weight: 100;
font-size: 1em;
}
</style>