thumbnail stretching vertically when resizing using keen slider - javascript

I used Keen Slider and implement this example on my page - https://codesandbox.io/s/github/rcbyr/keen-slider-sandboxes/tree/v6/navigation-controls/thumbnails/react?file=/src/App.js.
I created my own layout wherein the thumbnail is vertical and on the left side and the main banner is on the right. I don't have any problem displaying the layout and all functions are working as expected but when I resized my screen, both thumbnail images and the main banner image is being stretched vertically.
I added a fixed height on the thumbnail container and overflow but the issue still persists.
Here is the snippet of what I did:
function ThumbnailPlugin(mainRef) {
return (slider) => {
function removeActive() {
slider.slides.forEach((slide) => {
slide.classList.remove("active")
})
}
function addActive(idx) {
slider.slides[idx].classList.add("active")
}
function addClickEvents() {
slider.slides.forEach((slide, idx) => {
slide.addEventListener("click", () => {
if (mainRef.current) mainRef.current.moveToIdx(idx)
})
})
}
slider.on("created", () => {
if (!mainRef.current) return
addActive(slider.track.details.rel)
addClickEvents()
mainRef.current.on("animationStarted", (main) => {
removeActive()
const next = main.animator.targetIdx || 0
addActive(main.track.absToRel(next))
slider.moveToIdx(Math.min(slider.track.details.maxIdx, next))
})
})
}
}
function SingleProductBanner() {
const [sliderRef, instanceRef] = useKeenSlider({
initial: 0,
})
const [thumbnailRef] = useKeenSlider(
{
initial: 0,
renderMode: "performance",
slides: {
perView: 5,
spacing: 10
},
vertical: true // added this to make my thumbnail vertical
},
[ThumbnailPlugin(instanceRef)]
)
return (
<div id="single-product-carousel" className="row">
<div ref={sliderRef} className="keen-slider main-banner">
<div className="keen-slider__slide number-slide1">1</div>
<div className="keen-slider__slide number-slide2">2</div>
<div className="keen-slider__slide number-slide3">3</div>
<div className="keen-slider__slide number-slide4">4</div>
<div className="keen-slider__slide number-slide5">5</div>
<div className="keen-slider__slide number-slide6">6</div>
</div>
<div ref={thumbnailRef} className="keen-slider thumbnail">
<div className="keen-slider__slide number-slide1">1</div>
<div className="keen-slider__slide number-slide2">2</div>
<div className="keen-slider__slide number-slide3">3</div>
<div className="keen-slider__slide number-slide4">4</div>
<div className="keen-slider__slide number-slide5">5</div>
<div className="keen-slider__slide number-slide6">6</div>
</div>
</div>
)
}
export default SingleProductBanner
And here's CSS
#single-product-carousel {
padding: 35px;
border: 1px solid red;
[class^="number-slide"],
[class*=" number-slide"] {
background: grey;
display: flex;
align-items: center;
justify-content: center;
font-size: 50px;
color: #fff;
font-weight: 500;
height: 535px;
}
.number-slide1 {
background: rgb(64, 175, 255);
background: linear-gradient(
128deg,
rgba(64, 175, 255, 1) 0%,
rgba(63, 97, 255, 1) 100%
);
}
....
.main-banner {
width: 80% !important;
}
.thumbnail {
width: 20% !important;
overflow: hidden;
}
.thumbnail .keen-slider__slide {
font-size: 30px;
margin-top: 10px;
height: 20px;
overflow: hidden;
}
.thumbnail .keen-slider__slide {
cursor: pointer;
}
.thumbnail .keen-slider__slide.active {
border: 2px dashed black;
}
}
Here's the issue I am facing right now: https://www.awesomescreenshot.com/video/14510648?key=dcf786d6edaafa3ff02a078a7bcb3bf1

Related

Horizontal scroll areas with buttons and gradients

This is my code so far:
// Show and hide gradients
$(document).ready(function() {
$(".scroll-area").each(function(index) {
if ($(this)[0].scrollWidth <= $(this)[0].clientWidth) {
$(this).closest(".container").find(".left").css("display", "none");
$(this).closest(".container").find(".right").css("display", "none");
} else {
$(this).scroll(function() {
if ($(this)[0].scrollWidth > $(this)[0].clientWidth) {
if ($(this).scrollLeft() > 0) {
$(this).closest(".container").find(".left").css("display", "block");
}
if ($(this).scrollLeft() == 0) {
$(this).closest(".container").find(".left").css("display", "none");
}
var fullWidth = $(this)[0].scrollWidth - $(this)[0].offsetWidth - 1;
if ($(this).scrollLeft() >= fullWidth) {
$(this).closest(".container").find(".right").css("display", "none");
}
if ($(this).scrollLeft() < fullWidth) {
$(this).closest(".container").find(".right").css("display", "block");
}
}
});
}
});
});
// Scroll buttons
let interval;
$('.scroll-btn').on('mousedown', ({
target
}) => {
const type = $(target).attr('id');
interval = setInterval(() => {
var x = $('#a').scrollLeft();
$('#a').scrollLeft(type === 'left-arrow' ? x - 10 : x + 10);
}, 50);
});
$('.scroll-btn').on('mouseup', () => clearInterval(interval));
* {
margin: 0;
padding: 0;
font-family: sans-serif;
font-size: 16px;
}
.container {
width: 550px;
height: 80px;
background-color: grey;
position: relative;
margin-bottom: 20px;
}
.scroll-area {
white-space: nowrap;
overflow-x: auto;
height: 100%;
}
.left,
.right {
width: 50px;
height: 100%;
position: absolute;
pointer-events: none;
top: 0;
}
.left {
background: linear-gradient(90deg, orange 0%, rgba(0, 0, 0, 0) 100%);
left: 0;
display: none;
}
.right {
background: linear-gradient(-90deg, orange 0%, rgba(0, 0, 0, 0) 100%);
right: 0;
}
.arrow {
display: block;
position: absolute;
display: flex;
align-items: center;
justify-content: center;
height: 100%;
width: 15px;
cursor: pointer;
}
.left-arrow {
left: 0;
}
.right-arrow {
right: 0;
}
.left-arrow div,
.right-arrow div {
font-size: 40px;
}
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<div class="container">
<div id="x" class="left"></div>
<div class="right"></div>
<div class="arrow left-arrow">
<div class="scroll-btn" id="left-arrow">
<</div>
</div>
<div class="arrow right-arrow">
<div class="scroll-btn" id="right-arrow">></div>
</div>
<div id="a" class="scroll-area">
<div class="text">Scroll to right. The gradients and arrows should appear and disappear based on the scroll position. It should work with more than one container. Lorem ipsum.</div>
</div>
</div>
The needs are:
The arrows should appear and disappear in the same way like the gradients.
If there is not enough text to cause a scrollable area, there should be no gradient and now arrow.
There should be more than one container in the end.
Can somebody help me to do that? I would be super thankful!
You can put your arrows inside the left/right gradient divs. That way they will show/hide same way as the gradients.
EDIT
I cleaned up the code a bit since the original answer was kinda messy. (or 'weird' as mstephen19 put it :)).
// Show gradient and left/right arrows only if scrollable
$(".scroll-area").each((i, el) => {
$(el).parent().find(".right")[el.scrollWidth > el.clientWidth ? "show" : "hide"]();
});
// Show/hide gradient and arrows on scroll
$('.scroll-area').scroll((e) => {
const fullWidth = $(e.target)[0].scrollWidth - $(e.target)[0].offsetWidth - 1;
const left = $(e.target).scrollLeft()
$(e.target).parent().find(".left, .left-arrow")[left > 0 ? "show" : "hide"]();
$(e.target).parent().find(".right, .right-arrow")[left < fullWidth ? "show" : "hide"]();
});
// Scroll on left/right arrow mouse down
let intervalId;
$(".left-arrow, .right-arrow").on("mousedown", (e) => {
const scroll = $(e.target).closest(".container").find(".scroll-area");
intervalId = setInterval(() => {
const left = scroll.scrollLeft();
scroll.scrollLeft(e.target.classList.contains("left-arrow") ? left - 10 : left + 10);
}, 50);
}).on("mouseup mouseleave", () => {
clearInterval(intervalId);
});
* {
margin: 0;
padding: 0;
font-family: sans-serif;
font-size: 16px;
}
.container {
width: 550px;
height: 80px;
background-color: grey;
position: relative;
margin-bottom: 20px;
margin-left: 20px;
}
.scroll-area {
white-space: nowrap;
overflow-x: auto;
height: 100%;
}
.left,
.right {
width: 50px;
height: 100%;
position: absolute;
top: 0;
}
.left {
background: linear-gradient(90deg, orange 0%, rgba(0, 0, 0, 0) 100%);
left: 0;
display: none;
}
.right {
background: linear-gradient(-90deg, orange 0%, rgba(0, 0, 0, 0) 100%);
right: 0;
text-align: right;
}
.left-arrow,
.right-arrow {
margin: 0 10px;
position: absolute;
top: 50%;
-ms-transform: translateY(-50%);
transform: translateY(-50%);
cursor: pointer;
user-select: none;
font-size: 40px
}
.left-arrow {
display: none;
left: -25px;
}
.right-arrow {
right: -25px;
}
<html>
<head>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
</head>
<body>
<div class="container">
<div class="left"></div>
<div class="right"></div>
<div class="left-arrow"><</div>
<div class="right-arrow">></div>
<div class="scroll-area">
<div class="text">Scroll to right. The gradients and arrows should appear and disappear based on the scroll position. It should work with more than one container. Lorem ipsum.</div>
</div>
</div>
<div class="container">
<div class="left"><span class="left-arrow"><</span></div>
<div class="right"><span class="right-arrow">></span></div>
<div class="scroll-area">
<div class="text">No scroll.</div>
</div>
</div>
</body>
</html>
Some things about your code:
Your original code would not work with multiple containers, because you had a hardcoded #a ID in the interval code. You should really only have IDs on one element ideally, anyways (they're unique identifiers, while classes can be placed on multiple elements). The .scroll-area element should be found based on the target clicked.
You should combine your gradient and arrow elements into one element. By that, I mean making the div in which the arrow lives should be a child of the gradient div. Why manage them both separately?
Use class adding/removing/toggling instead of directly setting the CSS. Remember - when you find yourself writing the same code multiple times, it usually means there is a way to condense it down and make your code more dry and easier to understand + read.
Don't use the literal < and > symbols, as it can confuse some browsers. Use < and > instead.
Rather than toggling display to none and block, it's better to use visibility in this specific case. In my example, we use opacity for a fun fading effect.
Don't forget to listen for both mouseup mouseout events :)
Here is the working solution. I've refactored the code a bit:
let interval;
$('.arrow').on('mousedown', ({ target }) => {
const type = target.classList[1];
const scrollArea = $(target).parent().find('.scroll-area');
interval = setInterval(() => {
const prev = scrollArea.scrollLeft();
scrollArea.scrollLeft(type === 'left-arrow' ? prev - 10 : prev + 10);
}, 50);
});
$('.arrow').on('mouseup mouseout', () => clearInterval(interval));
$('.scroll-area').on('scroll', ({ target }) => {
const left = $(target).parent().find('.left-arrow');
const right = $(target).parent().find('.right-arrow');
const scroll = $(target).scrollLeft();
const fullWidth = $(target)[0].scrollWidth - $(target)[0].offsetWidth;
if (scroll === 0) left.addClass('hide');
else left.removeClass('hide');
if (scroll > fullWidth) right.addClass('hide');
else right.removeClass('hide');
});
.container {
width: 550px;
height: 80px;
background: grey;
position: relative;
}
.right-arrow,
.left-arrow {
height: 100%;
width: 50px;
position: absolute;
display: flex;
justify-content: center;
align-items: center;
font-size: 2rem;
cursor: pointer;
transition: all 0.2s linear;
}
.scroll-area {
white-space: nowrap;
overflow-x: scroll;
height: 100%;
}
.right-arrow {
background: linear-gradient(-90deg, orange 0%, rgba(0, 0, 0, 0) 100%);
left: 500px;
}
.left-arrow {
background: linear-gradient(90deg, orange 0%, rgba(0, 0, 0, 0) 100%);
left: 0px;
}
.scroll-btn {
pointer-events: none;
}
.hide {
opacity: 0;
}
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<div class="container">
<div class="arrow left-arrow">
<div class="scroll-btn" id="left-arrow"><</div>
</div>
<div class="arrow right-arrow">
<div class="scroll-btn" id="right-arrow">></div>
</div>
<div class="scroll-area">
<div class="text">
Scroll to right. The gradients and arrows should appear and disappear based on the scroll position. It should work with more than one
container. Lorem ipsum.
</div>
</div>
</div>
PS: If you don't like the fade effect, remove the transition: all 0.2s linear; part of the CSS, and switch .hide's opacity: 0 to visibility: hidden.

Creating an animation for a basic React slot machine

I'm quite new to react and a simple slot machine is my first mini-project. Currently I have completed the logic for displaying random emojis after the button is pressed. The next step for me, before styling and adding logic for winning/losing and a counter for coins etc is adding an animation.
https://codepen.io/fmressel/pen/vRLerN
This codepen is exactly the sort of thing I'm after, but as you can see it is structured quite differently to my code (below), and I'm pulling my hair out trying to figure out how I can get something similar to work for my project.
Any help much appreciated!
import React, { Component } from 'react'
import Contents from './Contents'
import './mainslots.css'
class Slots extends Component {
static defaultProps = {
fruits: ["🍒", "🍉", "🍊", "🍓", "🍇", "🥝"]
};
constructor(props) {
super(props);
this.state = {fruit1: '🍒', fruit2: '🍒', fruit3: '🍒', rolling: false};
this.roll = this.roll.bind(this);
};
roll = () => {
const dFruit1 = this.props.fruits[
Math.floor(Math.random() * this.props.fruits.length)
];
const dFruit2 = this.props.fruits[
Math.floor(Math.random() * this.props.fruits.length)
];
const dFruit3 = this.props.fruits[
Math.floor(Math.random() * this.props.fruits.length)
];
this.setState({fruit1: dFruit1, fruit2: dFruit2, fruit3: dFruit3, rolling: true});
setTimeout(() => {
this.setState({ rolling: false });
}, 700)
}
render(){
return(
<div className="SlotMachine">
<div className="SlotsContainer">
{this.state.fruit1}
{this.state.fruit2}
{this.state.fruit3}
</div>
<button className="spinner" onClick={this.roll} disabled={this.state.rolling}>
{this.state.rolling ? "Spinning..." : "Spin"}
</button>
</div>
);
}
}
export default Slots;
import React, { Component } from 'react'
class Contents extends Component {
Fruits = ["🍒", "🍉", "🍊", "🍓", "🍇", "🥝"];
render() {
return(
<div className="emptys">
{this.props.roll}
</div>
)
}
}
export default Contents
Here you go,
It was fun to develop :), you can run the below code snippet to review and I've added the comments in code, that will make things clear, please have a look,
Hope this will help,
const { createRef , Component } = React;
class Slots extends Component {
static defaultProps = {
fruits: ["🍒", "🍉", "🍊", "🍓", "🍇", "🥝"]
};
constructor(props) {
super(props);
this.state = { fruit1: "🍒", fruit2: "🍒", fruit3: "🍒", rolling: false };
// get ref of dic onn which elements will roll
this.slotRef = [createRef(), createRef(), createRef()];
}
// to trigger roolling and maintain state
roll = () => {
this.setState({
rolling: true
});
setTimeout(() => {
this.setState({ rolling: false });
}, 700);
// looping through all 3 slots to start rolling
this.slotRef.forEach((slot, i) => {
// this will trigger rolling effect
const selected = this.triggerSlotRotation(slot.current);
this.setState({ [`fruit${i + 1}`]: selected });
});
};
// this will create a rolling effect and return random selected option
triggerSlotRotation = ref => {
function setTop(top) {
ref.style.top = `${top}px`;
}
let options = ref.children;
let randomOption = Math.floor(
Math.random() * Slots.defaultProps.fruits.length
);
let choosenOption = options[randomOption];
setTop(-choosenOption.offsetTop + 2);
return Slots.defaultProps.fruits[randomOption];
};
render() {
return (
<div className="SlotMachine">
<div className="slot">
<section>
<div className="container" ref={this.slotRef[0]}>
{Slots.defaultProps.fruits.map((fruit, i) => (
<div key={i}>
<span>{fruit}</span>
</div>
))}
</div>
</section>
</div>
<div className="slot">
<section>
<div className="container" ref={this.slotRef[1]}>
{Slots.defaultProps.fruits.map(fruit => (
<div>
<span>{fruit}</span>
</div>
))}
</div>
</section>
</div>
<div className="slot">
<section>
<div className="container" ref={this.slotRef[2]}>
{Slots.defaultProps.fruits.map(fruit => (
<div>
<span>{fruit}</span>
</div>
))}
</div>
</section>
</div>
<div
className={!this.state.rolling ? "roll rolling" : "roll"}
onClick={!this.state.rolling && this.roll}
disabled={this.state.rolling}
>
{this.state.rolling ? "Rolling..." : "ROLL"}
</div>
</div>
);
}
}
ReactDOM.render(<Slots />, document.getElementById('react-root'));
.App {
font-family: sans-serif;
text-align: center;
}
.slot {
position: relative;
display: inline-block;
height: 100px;
width: 80px;
}
section {
position: absolute;
border-radius: 15px !important;
border: 3px solid black !important;
width: 70px;
height: 70px;
overflow: hidden;
background-color: grey;
border-radius: 2px;
border: 1px solid lightgrey;
color: white;
font-family: sans-serif;
text-align: center;
font-size: 25px;
line-height: 60px;
cursor: default;
}
.container {
position: absolute;
top: 2px;
width: 100%;
transition: top ease-in-out 0.5s;
text-align: center;
}
.roll {
width: 215px;
cursor: pointer;
background-color: yellow;
padding: 10px;
text-align: center;
font-size: 20px;
border-radius: 20px;
border: 3px solid black;
}
.rolling {
animation: blinkingText 1.2s infinite;
}
#keyframes blinkingText {
0% {
color: #000;
}
49% {
color: #000;
}
60% {
color: transparent;
}
99% {
color: transparent;
}
100% {
color: #000;
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react-root"></div>
I recreated the #VivekDoshi 's answer as a react FC.
const { useRef, useState} = React;
function Slots(){
const [fruit1,setFruit1] = useState("🍒");
const [fruit2,setFruit2] = useState("🍒");
const [fruit3,setFruit3] = useState("🍒");
const [rolling,setRolling] = useState(false);
let slotRef = [useRef(null), useRef(null), useRef(null)];
const fruits = ["🍒", "🍉", "🍊", "🍓", "🍇", "🥝"]
// to trigger roolling and maintain state
const roll = () => {
setRolling(true);
setTimeout(() => {
setRolling(false);
}, 700);
// looping through all 3 slots to start rolling
slotRef.forEach((slot, i) => {
// this will trigger rolling effect
const selected = triggerSlotRotation(slot.current);
if(i+1 == 1)
setFruit1(selected);
else if(i+1 == 2)
setFruit2(selected);
else
setFruit3(selected);
});
};
// this will create a rolling effect and return random selected option
const triggerSlotRotation = ref => {
function setTop(top) {
ref.style.top = `${top}px`;
}
let options = ref.children;
let randomOption = Math.floor(
Math.random() * fruits.length
);
let choosenOption = options[randomOption];
setTop(-choosenOption.offsetTop + 2);
return fruits[randomOption];
};
return (
<div className="SlotMachine">
<div className="slot">
<section>
<div className="container" ref={slotRef[0]}>
{fruits.map((fruit, i) => (
<div key={i}>
<span>{fruit}</span>
</div>
))}
</div>
</section>
</div>
<div className="slot">
<section>
<div className="container" ref={slotRef[1]}>
{fruits.map(fruit => (
<div>
<span>{fruit}</span>
</div>
))}
</div>
</section>
</div>
<div className="slot">
<section>
<div className="container" ref={slotRef[2]}>
{fruits.map(fruit => (
<div>
<span>{fruit}</span>
</div>
))}
</div>
</section>
</div>
<div
className={!rolling ? "roll rolling" : "roll"}
onClick={!rolling && roll}
disabled={rolling}>
{rolling ? "Rolling..." : "ROLL"}
</div>
</div>
);
};
ReactDOM.render(<Slots />, document.getElementById('react-root'));
.App {
font-family: sans-serif;
text-align: center;
}
.slot {
position: relative;
display: inline-block;
height: 100px;
width: 80px;
}
section {
position: absolute;
border-radius: 15px !important;
border: 3px solid black !important;
width: 70px;
height: 70px;
overflow: hidden;
background-color: grey;
border-radius: 2px;
border: 1px solid lightgrey;
color: white;
font-family: sans-serif;
text-align: center;
font-size: 25px;
line-height: 60px;
cursor: default;
}
.container {
position: absolute;
top: 2px;
width: 100%;
transition: top ease-in-out 0.5s;
text-align: center;
}
.roll {
width: 215px;
cursor: pointer;
background-color: yellow;
padding: 10px;
text-align: center;
font-size: 20px;
border-radius: 20px;
border: 3px solid black;
}
.rolling {
animation: blinkingText 1.2s infinite;
}
#keyframes blinkingText {
0% {
color: #000;
}
49% {
color: #000;
}
60% {
color: transparent;
}
99% {
color: transparent;
}
100% {
color: #000;
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="react-root"></div>

How to adjust div height by dragging whole bottom border line instead of bottom right corner in react?

I have one react component. I am able to adjust the height by adding resize: vertical;
overflow: auto; in my css file. However, I only can adjust by dragging bottom right corner of the border. Is it possible to change it to whole bottom line? Or any other react api can achieve this?
This is an example of how I adjust the height by dragging bottom right corner.
This is my component.
<div class='map'>
<MyMapComponent
handleChange={this.handleChange}
handleSubmit={this.handleSubmit}
date ={this.state.date}
getMap={this.getMap}
updateStatus = {this.state.updateStatus}
filter={this.state.filter}
filterList={this.state.filterList}
inputProps={{
classes: {
input: classes.multilineColor
}
}}
>
{/*this.getMap()*/}
</MyMapComponent>
</div>
This is my css file.
.map{
border: 2px solid;
padding: 20px;
width: 300px;
resize: vertical;
overflow: auto;
}
I follow this horizontal resize panel example to create a vertical one.
This is my edited resize panel
import React from "react"
import ReactDOM from "react-dom";
import './ResizablePanels.css';
class ResizablePanels extends React.Component {
eventHandler = null
constructor () {
super()
this.state = {
isDragging: false,
panels: [800, 300, 0]
}
}
componentDidMount () {
ReactDOM.findDOMNode(this).addEventListener('mousemove', this.resizePanel)
ReactDOM.findDOMNode(this).addEventListener('mouseup', this.stopResize)
ReactDOM.findDOMNode(this).addEventListener('mouseleave', this.stopResize)
}
startResize = (event, index) => {
this.setState({
isDragging: true,
currentPanel: index,
initialPos: event.clientY
})
}
stopResize = () => {
if (this.state.isDragging) {
console.log(this.state)
this.setState(({panels, currentPanel, delta}) => ({
isDragging: false,
panels: {
...panels,
[currentPanel]: (panels[currentPanel] || 0) - delta,
[currentPanel - 1]: (panels[currentPanel - 1] || 0) + delta
},
delta: 0,
currentPanel: null
}))
console.log(this.state)
}
}
resizePanel = (event) => {
if (this.state.isDragging) {
//console.log(event.clientY +" "+this.state.initialPos);
const delta = event.clientY - this.state.initialPos
this.setState({
delta: delta
})
}
}
render() {
const rest = this.props.children.slice(1);
// console.log(this.props);
return (
<div className="panel-container" onMouseUp={() => this.stopResize()}>
<div className="panel" style={{height: this.state.panels[0]}}>
{this.props.children[0]}
</div>
{[].concat(...rest.map((child, i) => {
return [
<div onMouseDown={(e) => this.startResize(e, i + 1)}
key={"resizer_" + i}
style={this.state.currentPanel === i+1 ? {top: this.state.delta} : {}}
className="resizer"></div>,
<div key={"panel_" + i} className="panel" style={{height: this.state.panels[i + 1]}}>
{child}
</div>
]
}))}
</div>
)
}
}
export default ResizablePanels;
and this is my css file for this panel
#import url('https://fonts.googleapis.com/css?family=Ubuntu');
html {
background: #333;
font-family: sans-serif;
}
h1 {
color: white;
}
.panel-container {
display: flex;
min-height: 100%;
justify-content: center;
flex-direction: column;
text-align: center;
}
.panel {
background: #EEE;
border: 0px solid gray;
padding: 1px;
}
.panel:first-child {
}
.resizer {
height: 8px;
background: darkGray;
position: relative;
cursor: row-resize;
flex-shrink: 0;
-webkit-user-select: none; /* Chrome all / Safari all */
-moz-user-select: none; /* Firefox all */
-ms-user-select: none; /* IE 10+ */
user-select: none; /* Likely future */
}
.resizer::after,
.resizer::before {
content: "";
border-left: 1px solid #333;
position: absolute;
top: 50%;
transform: translateX(-100%);
right: 0;
display: inline-block;
height: 20px;
margin: 0 2px;
}
.resizer::before {
top: 0;
}

How we can have the real color on an picture thumbnail when we click on it?

For my project here I tried to have a color picture when I clicked on one of the four different pictures.
The four different picture are thumbnails, and when I click on a thumbnail, this one is active and I want to have this active thumbnail with the right logo color, like the Photoshop profile here. And the other Thumbnails and in grey, because we didn't click on it so they are considered inactive.
So I tried to do this with filter property and I didn't find the right solution.
So can you help me please to find the right solution?
I can show you my code of my project :
Vue.component('carousel', {
template: `
<div class="card-carousel" >
<div class="thumbnails">
<div
v-for="(image, index) in images"
:key="image.id"
:class="['thumbnail-image', (activeImage == index) ? 'active' : '']"
#click="activateImage(index)">
<img :src="image.thumb" class="active"/>
</div>
</div>
<div class="containe-carousel">
<span id = "textespan"> {{currentImage.text}}</span>
<div class="photoshop-screenshot">
<img :src="currentImage.big" alt="">
</div>
<div class="card-img">
<img :src="currentImage2.big2" alt="">
</div>
</div>
</div>
`,
computed: {
currentImage() {
return this.images[this.activeImage];
},
currentImage2() {
return this.images[this.activeImage];
}
},
data() {
return {
activeImage: 0,
}
},
methods: {
activateImage(imageIndex) {
this.activeImage = imageIndex;
},
},
props: ['images']
});
.section{
background-color: black;
}
.card-carousel {
user-select: none;
position: relative;
}
.containe-carousel {
padding-top: 5%;
}
.thumbnails {
display: flex;
justify-content: space-evenly;
flex-direction: row;
}
.thumbnail-image {
display: fixed;
align-items: center;
cursor: pointer;
padding: 2px;
}
.thumbnail-image > img {
width: 100%;
height: auto;
transition: all 250ms;
filter: grayscale(100%);
}
.thumbnail-image:selected> img {
box-shadow: 2px 2px 6px 1px rgba(0,0,0, 0.5);
visibility: hidden;
filter: none;
}
.card-img {
position: relative;
}
.card-img > img {
margin: 0 auto;
padding-top: 7%;
z-index: 2;
}
.photoshop-screenshot {
position:absolute;
z-index: 1;
width: 65%;
left:50%;top:75%;
}
.active{
filter: contrast(1000%)
/* black to white */
invert(100%)
/* white to off-white */
sepia(100%)
/* off-white to yellow */
saturate(10000%)
/* do whatever you want with yellow */
hue-rotate(90deg);
}
#textespan {
text-align: center;
font-size: 300%;
}
.containe-carousel span {
color: white;
font-weight: bold;
}
<section class="section" id="app">
<div class="container">
<div class="text-center" style="margin:0px 50px">
<div class="heading-underscore">
<h2 class="dk-5q-color">
<?php say("X50Q-dashboard-title"); ?>
</h2>
</div>
</div>
<div class="columns">
<div class="column ">
<div class="card-content">
<carousel
:starting-image="0"
:show-progress-bar="true"
:images="images"
></carousel>
</div>
</div>
</div>
</div>
</section>
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.13/dist/vue.js"></script>
<script src ="/x/x50q-rgb-mechanical-keyboard/x50q-cloud-js.js"></script>
<script>
var app = new Vue({
el: '#app',
data() {
return {
images: [
{
text : 'Photoshop',
id: '1',
big: '/images/das-keyboard-x50q/photoshop-profile.PNG',
big2: '/images/das-keyboard-x50q/photoshop-screenshot.png',
thumb: '/images/das-keyboard-x50q/photoshop-logo.jpg'
},
{
text : 'Aurocad',
id: '2',
big: '/images/das-keyboard-x50q/autocad-profile.png',
big2: '/images/das-keyboard-x50q/autocad-screenshot.png',
thumb: '/images/das-keyboard-x50q/autocad-logo.png'
},
{
text : ' Counter-Strike',
id: '3',
big: '/images/das-keyboard-x50q/counterstrike-profile.png',
big2: '/images/das-keyboard-x50q/counterstrike-screenshot.jpg',
thumb: '/images/das-keyboard-x50q/counterstrike-logo.png'
},
{
text : 'League of Legends',
id: '4',
big: '/images/das-keyboard-x50q/leagueoflegends-profile.png',
big2: '/images/das-keyboard-x50q/leagueoflegends-screenshot.png',
thumb: '/images/das-keyboard-x50q/leagueoflegends-logo.jpg'
}
],
}
}
});
</script>
Problem 1: .active filter does not remove grayscale
When the image is clicked, you apply an .active class, which applies an exaggerated filter (making it look like a photo negative) instead of removing the grayscale:
.active {
filter: contrast(1000%)
/* black to white */
invert(100%)
/* white to off-white */
sepia(100%)
/* off-white to yellow */
saturate(10000%)
/* do whatever you want with yellow */
hue-rotate(90deg);
}
The CSS should be either:
filter: grayscale(0);
OR to remove all filters:
filter: none;
Problem 2: All thumbnails are .active
While the .active class is dynamically added to .thumbnail-image, it's also statically applied to all thumbnails (the img child element). This will make all images appear in the "active" state all the time. The static class should be removed:
<!-- <img :src="image.thumb" class="active"/> --> <!-- DON'T DO THIS -->
<img :src="image.thumb">
Alternatively, you can move the dynamic setting of .thumbnail-image and .active from the container div to the img itself.
Problem 3: .active does not target img
The .active class is applied to .thumbnail-image (the img container), but you really want to apply the filter to the img child element, so you'll have to modify your CSS selector to this:
.active > img {
...
}
If you opted to move the dynamic class setting to the img itself (as suggested previously above), you don't have to modify the selector here.
Demo of fix
Vue.component("carousel", {
template: `
<div class="card-carousel" >
<div class="thumbnails">
<div v-for="(image, index) in images"
:key="image.id"
:class="['thumbnail-image', (activeImage == index) ? 'active' : '']"
#click="activateImage(index)">
<img :src="image.thumb" />
</div>
</div>
<div class="containe-carousel">
<span id="textespan"> {{currentImage.text}}</span>
<div class="photoshop-screenshot">
<img :src="currentImage.big" alt="">
</div>
<div class="card-img">
<img :src="currentImage2.big2" alt="">
</div>
</div>
</div>
`,
computed: {
currentImage() {
return this.images[this.activeImage];
},
currentImage2() {
return this.images[this.activeImage];
}
},
data() {
return {
activeImage: 0
};
},
methods: {
activateImage(imageIndex) {
this.activeImage = imageIndex;
}
},
props: ["images"]
});
new Vue({
el: "#app",
data: () => ({
images: [
{
text: "Photoshop",
id: "1",
big: "//placekitten.com/201/201",
big2: "//placekitten.com/201/201",
thumb: "//placekitten.com/201/201"
},
{
text: "Aurocad",
id: "2",
big: "//placekitten.com/202/202",
big2: "//placekitten.com/202/202",
thumb: "//placekitten.com/202/202"
},
{
text: " Counter-Strike",
id: "3",
big: "//placekitten.com/203/203",
big2: "//placekitten.com/203/203",
thumb: "//placekitten.com/203/203"
},
{
text: "League of Legends",
id: "4",
big: "//placekitten.com/204/204",
big2: "//placekitten.com/204/204",
thumb: "//placekitten.com/204/204"
}
]
})
});
.section {
background-color: black;
}
.card-carousel {
user-select: none;
position: relative;
}
.containe-carousel {
padding-top: 5%;
}
.thumbnails {
display: flex;
justify-content: space-evenly;
flex-direction: row;
}
.thumbnail-image {
display: fixed;
align-items: center;
cursor: pointer;
padding: 2px;
}
.thumbnail-image > img {
width: 100%;
height: auto;
transition: all 250ms;
filter: grayscale(100%);
}
.thumbnail-image:selected > img {
box-shadow: 2px 2px 6px 1px rgba(0, 0, 0, 0.5);
visibility: hidden;
filter: none;
}
.card-img {
position: relative;
}
.card-img > img {
margin: 0 auto;
padding-top: 7%;
z-index: 2;
}
/* .photoshop-screenshot {
position: absolute;
z-index: 1;
width: 65%;
left: 50%;
top: 75%;
} */
.active > img {
/* filter: contrast(1000%) invert(100%) sepia(100%) saturate(10000%)
hue-rotate(90deg); */
filter: grayscale(0);
}
#textespan {
text-align: center;
font-size: 300%;
}
.containe-carousel span {
color: white;
font-weight: bold;
}
<script src="https://unpkg.com/vue#2.5.17"></script>
<section class="section" id="app">
<carousel :starting-image="0"
:show-progress-bar="true"
:images="images"></carousel>
</section>
You only need CSS.
.thumbnail-image > img {
filter: grayscale(100%);
-webkit-filter: grayscale(100%);
}
.thumbnail-image > img:active {
filter: grayscale(0%);
-webkit-filter: grayscale(0%);
}
You can create a class without grayscale, and normal tag img with grayscale. Add class without grayscale,color in my example, for the clicked element, and iterate for others images and remove color class. Like this:
function onClick(it) {
const withColor = it.parentElement
.getElementsByClassName('color')[0];
if(withColor)
withColor.classList.remove('color');
it.classList.add( 'color' );
}
#images {
display: flex;
}
img {
margin: 10px;
width: 100px;
height: 100px;
filter: grayscale( 100% );
border: 3px solid transparent;
}
img.color {
filter: grayscale( 0% );
transition: filter 400ms;
border: 3px solid green;
border-radius: 2px
}
<div id="images">
<img src="https://i.stack.imgur.com/MagOw.jpg" class="image" onclick="onClick(this)">
<img src="https://i.stack.imgur.com/MagOw.jpg" class="image" onclick="onClick(this)">
<img src="https://i.stack.imgur.com/MagOw.jpg" class="image" onclick="onClick(this)">
</div>

How to keep user's scrolling place when resizing div

I wanted to do a cool menu effect for a website I'm working on. I'm having a div act as the the section for the main content. When the user opens the menu, the main content div will resize and move out of the way, revealing the menu. However, when I do this with the code I have written, it always loses my scrolling place on the page. Is there any way to keep my place on the page when it shrinks and also when it expands back again? Below is what I have. Thank you in advance!
function shrinkPage() {
var element = document.getElementById("mock-body");
element.classList.toggle("mock-body-on-burger");
var z = document.getElementById("mock-body-container");
z.classList.toggle("mock-body-container-on-burger");
var x = document.getElementById("body");
x.classList.toggle("body-on-burger");
};
body {
margin: 0;
background:#000;
}
.body-on-burger {
max-width: 100%;
overflow-x:hidden;
}
.mock-body-container{
height:100vh;
}
.mock-body-container-on-burger {
height:100vh;
transform: scale(0.4) translate(130%);
overflow: hidden;
}
.mock-body-size-change{
overflow: scroll;
}
.mock-body {
position:relative;
background: #fff;
margin-left: 50px;
}
.container {
position: fixed;
height:50px;
width:50px;
cursor: pointer;
}
.container #icon {
width: 16px;
height: 8px;
position: relative;
margin: 0px auto 0;
top: 40%;
}
.container #icon .bars {
height: 1px;
background: #fff;
}
.myDiv {
height:500px;
}
.one {
background:red;
}
.two {
background:green;
}
.three {
background:blue;
}
<body id="body">
<div class="menu-activator" onclick="shrinkPage()">
<div class="container usd">
<div id="icon">
<div class="bars first"></div>
</div>
</div>
</div>
<div id="mock-body-container" class="mock-body-container">
<div id="mock-body" class="mock-body">
<div class="myDiv one"></div>
<div class="myDiv two"></div>
<div class="myDiv three"></div>
</div>
</div>
</body>
Please take a look at the snippet below. Notice how the overflow property is used.
You have to scroll mock-body-container to keep its scrolling position.
You're scrolling body instead, so when you scale mock-body-container there is nothing to scroll in body and you loose the scrolling position.
function shrinkPage() {
var element = document.getElementById("mock-body");
element.classList.toggle("mock-body-on-burger");
var z = document.getElementById("mock-body-container");
z.classList.toggle("mock-body-container-on-burger");
var x = document.getElementById("body");
x.classList.toggle("body-on-burger");
};
body {
margin: 0;
background:#000;
}
.body-on-burger {
max-width: 100%;
overflow-x:hidden;
}
.mock-body-container{
height:100vh;
overflow:auto;
}
.mock-body-container-on-burger {
height:100vh;
transform: scale(0.4) translate(130%);
}
.mock-body-size-change{
overflow: scroll;
}
.mock-body {
position:relative;
background: #fff;
margin-left: 50px;
}
.container {
position: fixed;
height:50px;
width:50px;
cursor: pointer;
}
.container #icon {
width: 16px;
height: 8px;
position: relative;
margin: 0px auto 0;
top: 40%;
}
.container #icon .bars {
height: 1px;
background: #fff;
}
.myDiv {
height:500px;
}
.one {
background:red;
}
.two {
background:green;
}
.three {
background:blue;
}
<body id="body">
<div class="menu-activator" onclick="shrinkPage()">
<div class="container usd">
<div id="icon">
<div class="bars first"></div>
</div>
</div>
</div>
<div id="mock-body-container" class="mock-body-container">
<div id="mock-body" class="mock-body">
<div class="myDiv one"></div>
<div class="myDiv two"></div>
<div class="myDiv three"></div>
</div>
</div>
</body>
Once you know the element that was in focus it should be relatively easy. If you need to find which element was last in focus, you can do that with a scroll function. If you need this as well let me know and I will update my answer.
If you know that #mock-body is the last element in focus, just scroll back to it after the resize.
In this example I've used jQuery as it makes this interaction easier, but this can be done (albeit more verbosely) with vanilla JS as well.
$('html, body').animate({
scrollTop: $('#mock-body').offset().top
}, 0); // If you want the animation to be smoother you can increase 0 to a higher number
A simple way to do it is to remember the position of the document scroll and reapply it when you getting back to "normal" view:
let savedScroll;
function shrinkPage() {
let _s = (el) => document.querySelector(el),
s_ = (d) => !d.classList.contains('body-on-burger'),
x = _s('#body'),
element = _s('#mock-body'),
z = _s('#mock-body-container');
if (s_(x)) {
savedScroll = document.documentElement.scrollTop;
}
element.classList.toggle("mock-body-on-burger");
z.classList.toggle("mock-body-container-on-burger");
x.classList.toggle("body-on-burger");
if (s_(x)) {
document.documentElement.scrollTop = savedScroll;
}
};
Check it out:
let savedScroll;
function shrinkPage() {
let _s = (el) => document.querySelector(el),
s_ = (d) => !d.classList.contains('body-on-burger'),
x = _s('#body'),
element = _s('#mock-body'),
z = _s('#mock-body-container');
if (s_(x)) {
savedScroll = document.documentElement.scrollTop;
}
element.classList.toggle("mock-body-on-burger");
z.classList.toggle("mock-body-container-on-burger");
x.classList.toggle("body-on-burger");
if (s_(x)) {
document.documentElement.scrollTop = savedScroll;
}
};
body {
margin: 0;
background: #000;
}
.body-on-burger {
max-width: 100%;
overflow-x: hidden;
}
.mock-body-container {
height: 100vh;
}
.mock-body-container-on-burger {
height: 100vh;
transform: scale(0.4) translate(130%);
overflow: hidden;
}
.mock-body-size-change {
overflow: scroll;
}
.mock-body {
position: relative;
background: #fff;
margin-left: 50px;
}
.container {
position: fixed;
height: 50px;
width: 50px;
cursor: pointer;
}
.container #icon {
width: 16px;
height: 8px;
position: relative;
margin: 0px auto 0;
top: 40%;
}
.container #icon .bars {
height: 1px;
background: #fff;
}
.myDiv {
height: 500px;
}
.one {
background: red;
}
.two {
background: green;
}
.three {
background: blue;
}
<body id="body">
<div class="menu-activator" onclick="shrinkPage()">
<div class="container usd">
<div id="icon">
<div class="bars first"></div>
</div>
</div>
</div>
<div id="mock-body-container" class="mock-body-container">
<div id="mock-body" class="mock-body">
<div class="myDiv one"></div>
<div class="myDiv two"></div>
<div class="myDiv three"></div>
</div>
</div>
</body>
Legend: _s(el) returns first match of el and s_(d) checks if d has class body-on-burger.
The simple way to do this is to determine the change in height during the resize, and scroll that much.
const heightChange = newHeight - initialHeight;
scrollableDiv.scrollTop = scrollableDiv.scrollTop - heightChange;
In my case I am using a resize method I wrote, so I do this work inside of a window.addEventListener("mousemove", handleResize); when I know the div in actively being resized by the user.
This will still work fine with native html resizable elements, you just need to figure out how/when to listen for resize/drag events accordingly.

Categories

Resources