303 lines
6.6 KiB
Vue
303 lines
6.6 KiB
Vue
<template>
|
|
<div class="image-carousel">
|
|
<Loading v-if="!images.length" />
|
|
<div ref="background" class="background" />
|
|
<img ref="img" :src="imgURL" alt="Your carousel images"
|
|
:style="{display: !images.length ? 'none' : 'block'}">
|
|
|
|
<div class="row info-container" v-if="_showDate || _showTime">
|
|
<div class="col-6 weather-container">
|
|
<span v-if="!_showWeather"> </span>
|
|
<Weather :show-icon="_showWeatherIcon" :show-summary="_showWeatherSummary" :show-temperature="_showTemperature"
|
|
:icon-color="weatherIconColor" :icon-size="weatherIconSize" :animate="_animateWeatherIcon" v-else />
|
|
</div>
|
|
|
|
<div class="col-6 date-time-container">
|
|
<DateTime :show-date="_showDate" :show-time="_showTime" :show-seconds="_showSeconds"
|
|
v-if="_showTime || _showDate" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import Utils from "@/Utils";
|
|
import Loading from "@/components/Loading";
|
|
import DateTime from "@/components/widgets/DateTime/Index";
|
|
import Weather from "@/components/widgets/Weather/Index";
|
|
|
|
/**
|
|
* In order to use the image carousel widget you have to specify one or more
|
|
* resource directories in your backend.http configuration - that's needed to
|
|
* specify the external directories that will be exported through the web server.
|
|
* Example:
|
|
*
|
|
* ```yaml
|
|
* backend.http:
|
|
* resources:
|
|
* carousel: /mnt/hd/photos/albums/carousel
|
|
* ```
|
|
*/
|
|
export default {
|
|
name: "ImageCarousel",
|
|
components: {Weather, DateTime, Loading},
|
|
mixins: [Utils],
|
|
props: {
|
|
// Images directory
|
|
imgDir: {
|
|
type: String,
|
|
required: true,
|
|
},
|
|
|
|
// Refresh interval in seconds.
|
|
refreshSeconds: {
|
|
type: Number,
|
|
default: 15,
|
|
},
|
|
|
|
// Show the current date on top of the images
|
|
showDate: {
|
|
default: false,
|
|
},
|
|
|
|
// Show the current time on top of the images
|
|
showTime: {
|
|
default: false,
|
|
},
|
|
|
|
// If false then don't display the seconds.
|
|
showSeconds: {
|
|
default: false,
|
|
},
|
|
|
|
// If false then don't display weather info.
|
|
showWeather: {
|
|
default: false,
|
|
},
|
|
|
|
// If false then temperature won't be displayed.
|
|
showTemperature: {
|
|
default: true,
|
|
},
|
|
|
|
// If false then don't display the weather state icon.
|
|
showWeatherIcon: {
|
|
default: true,
|
|
},
|
|
|
|
// If false then don't display the weather summary text.
|
|
showWeatherSummary: {
|
|
default: true,
|
|
},
|
|
|
|
// Weather icon color.
|
|
weatherIconColor: {
|
|
type: String,
|
|
default: 'white',
|
|
},
|
|
|
|
// Size of the weather icon in pixels.
|
|
weatherIconSize: {
|
|
type: Number,
|
|
default: 70,
|
|
},
|
|
|
|
// If false then the weather icon will be animated.
|
|
// Otherwise, it will be a static image.
|
|
animateWeatherIcon: {
|
|
default: true,
|
|
},
|
|
},
|
|
|
|
data() {
|
|
return {
|
|
images: [],
|
|
currentImage: undefined,
|
|
loading: false,
|
|
}
|
|
},
|
|
|
|
computed: {
|
|
imgURL() {
|
|
let port = 8008
|
|
if ('backend.http' in this.$root.config && 'port' in this.$root.config['backend.http']) {
|
|
port = this.$root.config['backend.http'].port
|
|
}
|
|
|
|
return '//' + window.location.hostname + ':' + port + this.currentImage
|
|
},
|
|
|
|
_showDate() {
|
|
return this.parseBoolean(this.showDate)
|
|
},
|
|
|
|
_showTime() {
|
|
return this.parseBoolean(this.showTime)
|
|
},
|
|
|
|
_showSeconds() {
|
|
return this.parseBoolean(this.showSeconds)
|
|
},
|
|
|
|
_showTemperature() {
|
|
return this.parseBoolean(this.showTemperature)
|
|
},
|
|
|
|
_showWeather() {
|
|
return this.parseBoolean(this.showWeather)
|
|
},
|
|
|
|
_showWeatherIcon() {
|
|
return this.parseBoolean(this.showWeatherIcon)
|
|
},
|
|
|
|
_showWeatherSummary() {
|
|
return this.parseBoolean(this.showWeatherSummary)
|
|
},
|
|
|
|
_animateWeatherIcon() {
|
|
return this.parseBoolean(this.animateWeatherIcon)
|
|
}
|
|
},
|
|
|
|
methods: {
|
|
async refresh() {
|
|
if (!this.images.length) {
|
|
this.loading = true
|
|
|
|
try {
|
|
this.images = await this.request('utils.search_web_directory', {
|
|
directory: this.imgDir,
|
|
extensions: ['.jpg', '.jpeg', '.png'],
|
|
})
|
|
|
|
this.shuffleImages()
|
|
} finally {
|
|
this.loading = false
|
|
}
|
|
}
|
|
|
|
if (this.images.length) {
|
|
this.currentImage = this.images.pop()
|
|
}
|
|
},
|
|
|
|
onNewImage() {
|
|
if (!this.$refs.img)
|
|
return
|
|
|
|
this.$refs.background.style['background-image'] = 'url(' + this.imgURL + ')'
|
|
this.$refs.img.style.width = 'auto'
|
|
|
|
if (this.$refs.img.width > this.$refs.img.height) {
|
|
const ratio = this.$refs.img.width / this.$refs.img.height
|
|
if (ratio >= 4/3 && ratio <= 16/9) {
|
|
this.$refs.img.style.width = '100%'
|
|
} else if (ratio <= 4/3) {
|
|
this.$refs.img.style.height = '100%'
|
|
}
|
|
}
|
|
},
|
|
|
|
shuffleImages() {
|
|
for (let i=this.images.length-1; i > 0; i--) {
|
|
let j = Math.floor(Math.random() * (i+1))
|
|
let x = this.images[i]
|
|
this.images[i] = this.images[j]
|
|
this.images[j] = x
|
|
}
|
|
},
|
|
},
|
|
|
|
mounted() {
|
|
this.$refs.img.addEventListener('load', this.onNewImage)
|
|
this.$refs.img.addEventListener('error', this.refresh)
|
|
|
|
this.refresh()
|
|
setInterval(this.refresh, Math.round(this.refreshSeconds * 1000))
|
|
},
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.image-carousel {
|
|
width: calc(100% + 1.5em);
|
|
height: calc(100% + 1.5em);
|
|
position: relative;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
background-color: black;
|
|
margin: -0.75em 0.75em 0.75em -0.75em !important;
|
|
|
|
.background {
|
|
position: absolute;
|
|
top: 0;
|
|
width: 100%;
|
|
height: 100vh;
|
|
background-color: black;
|
|
background-position: center;
|
|
background-size: cover;
|
|
background-repeat: no-repeat;
|
|
filter: blur(13px);
|
|
-webkit-filter: blur(13px);
|
|
}
|
|
|
|
img {
|
|
position: absolute;
|
|
max-height: 100%;
|
|
z-index: 2;
|
|
}
|
|
}
|
|
|
|
.info-container {
|
|
width: 100%;
|
|
position: absolute;
|
|
bottom: 0;
|
|
display: flex;
|
|
align-items: flex-end;
|
|
z-index: 10;
|
|
color: white;
|
|
text-shadow: 3px 3px 4px black;
|
|
font-size: 1.25em;
|
|
margin: 0.5em;
|
|
padding: 0 1em;
|
|
|
|
.date-time {
|
|
text-align: right;
|
|
}
|
|
}
|
|
</style>
|
|
|
|
<style lang="scss">
|
|
.image-carousel {
|
|
.info-container {
|
|
.weather-container {
|
|
margin-bottom: 0.5em;
|
|
|
|
.weather {
|
|
font-size: 1.5em;
|
|
}
|
|
|
|
h1 {
|
|
justify-content: left;
|
|
margin-bottom: -0.5em;
|
|
font-size: 0.8em;
|
|
}
|
|
}
|
|
|
|
.date-time {
|
|
margin-right: 1em;
|
|
|
|
.date {
|
|
font-size: 2em;
|
|
}
|
|
|
|
.time {
|
|
font-size: 4em;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</style>
|