Zoom in and out on mouse click with CSS - css

I want to zoom image with only CSS. The code below zooms the image when the left button of the mouse is kept pressed but I want to zoom in and out with a mouse click. How can I achieve that?
.container img {
transition: transform 0.25s ease;
cursor: zoom-in;
}
.container img:active {
-webkit-transform: scale(2);
transform: scale(2);
cursor: zoom-out;
}

Let's use a trick here, an input checkbox:
input[type=checkbox] {
display: none;
}
.container img {
margin: 100px;
transition: transform 0.25s ease;
cursor: zoom-in;
}
input[type=checkbox]:checked ~ label > img {
transform: scale(2);
cursor: zoom-out;
}
<div class="container">
<input type="checkbox" id="zoomCheck">
<label for="zoomCheck">
<img src="https://via.placeholder.com/200">
</label>
</div>

Building on #Nhan answer: https://stackoverflow.com/a/39859268/661872
Shorter, scoped and does not require tracking ids for multiple elements.
.click-zoom input[type=checkbox] {
display: none
}
.click-zoom img {
margin: 100px;
transition: transform 0.25s ease;
cursor: zoom-in
}
.click-zoom input[type=checkbox]:checked~img {
transform: scale(2);
cursor: zoom-out
}
<div class="click-zoom">
<label>
<input type="checkbox">
<img src="https://via.placeholder.com/200">
</label>
</div>
<div class="click-zoom">
<label>
<input type="checkbox">
<img src="https://via.placeholder.com/200">
</label>
</div>

4 Ways to Add Click Events with Only CSS Pseudo-Selectors
Note: I'll be using the word target when referring to the element we want to manipulate and trigger as the element we are using to manipulate target.
:checked
Use checkboxes or radios and :checked to determine or cause a target's state and/or to take action.
Trigger
<label>
<input type="checkbox">
<!--or-->
<input type="radio">
Conditions
Requires that the target must be:
A sibling that follows the trigger or...
...a descendant of the trigger.
Note
Hide the actual <checkbox> with display:none
Ensure that the <checkbox> has an id and that the <label> has a for attribute with a value matching the id of the <checkbox>
This is dependant upon the target being a sibling that follows the trigger or the target as a descendant. Therefore be aware that you'll most likely use these selector combinators: ~, +, >.
HTML
<label for='chx'>CHX</label>
<input id='chx' type="checkbox">
<div>TARGET</div>
CSS
#chx:checked + div {...
:target
Use an <a>nchor and apply the :target pseudo-selector on the target element.
Trigger
Conditions
Assign an id to the target.
Assign that same id to the <a> href attribute preceding with a hash #
HTML
<a href='#target'>A</a>
<div id='target'>TARGET</div>
CSS
#target:target {...
:focus
The trigger element must be either an <input> type or have the attribute tabindex in order to use :focus.
Trigger
<div tabindex='0'>ANY INPUT OR USE TABINDEX</div>
Conditions
Target must a sibling that is located after the trigger or *target must be a descendant of the trigger.
State or effect will persist until user clicks elsewhere thereafter a blur or unfocus event will occur.
HTML
<nav tabindex='0'>
<a href='#/'>TARGET</a>
<a href='#/'>TARGET</a>
<a href='#/'>TARGET</a>
</nav>
CSS
nav:focus ~ a {...
:active
This is a hack that cleverly exploits the transition-delay property in order to actually have a persistent state achieved with no script.
Trigger
<a href='#/'>A</a>
Conditions
Target must a sibling that is located after the trigger or *target must be a descendant of the trigger.
There must be a transition assigned to the target twice.
The first one to represent the persistent state.
The second one to represent the normal state.
HTML
A
<div class='target'>TARGET</div>
CSS
.target {
opacity: 1;
transition: all 0s 9999999s;
}
a:active ~ .target {
opacity: 0;
transition: all 0s;
}
Wacked looking, right? I'll try to explain it, but you're better off reading this article.
Under normal circumstances, if your trigger had the :active pseudo-selector, we are able to manipulate the target upon keydown. That means our active state is actually active as long as you keep your finger on the button...that's crappy and useless, I mean what are you expected to do to make .active to be useful? Maybe a paperweight and some rubber bands to keep a steady and persistent pressure on the button?
We will leave .active the way it is: lame and useless. Instead:
Make a ruleset for target under normal circumstances. In the example above it's opacity:1.
Next we add a transition: ...ok then... all which works, next is 0s ...ok so this transition isn't going to be seen it's duration is 0 seconds, and finally... 9999999s ...116 days delay?
We'll come back to that, we will continue onto the next rulesets...
These rulesets declare what happens to target under the influence of trigger:active. As you can see that it just does what it normally does, which is onkeydown target will become invisible in 0 seconds. Now once the user keys up, target is visible again...no *target's * new state of opacity:0 is persistent! No paperweight, technology has come a long way.
The target is still actually going to revert back to it's normal state, because :active is too lazy and feeble to work without rubber bands and paperweights. The persistent state is perceived and not real because target is still leaving the state brought on by :active which will be about 116 days before that will happen. ;)
This Snippet features the 4 ways previously mentioned. I'm aware that the OP requested zoom (which is featured therein), but thought it would be to repetitive and boring, so I added different effects as well as zooming.
SNIPPET
a {
text-decoration: none;
padding: 5px 10px;
border:1px solid red;
margin: 10px 0;
display: inline-block;
}
label {
cursor: pointer;
padding: 5px 10px;
border: 1px solid blue;
margin: 10px 0;
display:inline-block;
}
button {
cursor:pointer;
padding: 5px 10px;
border: grey;
font:inherit;
display:inline-block;
}
img#img {
width: 384px;
height: 384px;
display: block;
object-fit: contain;
margin: 10px auto;
transition: width 3s height 3s ease-in;
opacity: 1;
transition: opacity 1s 99999999s;
}
#zoomIn,
#zoomOut,
#spin {
display: none;
padding: 0 5px;
}
#zoomOut:checked + img#img {
width: 128px;
height: 128px;
transition: all 3s ease-out;
}
#zoomIn:checked + img#img {
width: 512px;
height: 512px;
transition: all 3s ease-in-out;
}
#spin:checked ~ img#img {
transform: rotate(1440deg);
}
img#img:target {
box-shadow: 0px 8px 6px 3px rgba(50, 50, 50, 0.75);
}
a.out:focus ~ img#img {
opacity: 0;
transition: opacity 1s;
}
a.in:active ~ img#img {
opacity: 1;
transition: opacity 1s;
}
.grey:focus ~ img#img {
filter: grayscale(100%);
}
<a href='#/' class='out'>FadeouT</a><a href='#/' class='in'>FadeiN</a>
<a href='#img'>ShadoW</a>
<br/><button class='grey' tabindex='0'>GreyscalE</button><br/>
<label for='spin'>SpiN</label>
<input type='checkbox' id='spin'>
<label for='zoomIn'>ZoomiN</label>
<input type='radio' id='zoomIn' name='zoom'>
<label for='zoomOut'>ZoomouT</label>
<input type='radio' id='zoomOut' name='zoom'>
<img id='img' src='https://i.ibb.co/5LPXSfn/Lenna-test-image.png'>

.container img {
margin: 100px;
transition: transform 0.25s ease;
cursor: zoom-in;
}
input[type=checkbox]:checked ~ label > img {
transform: scale(2);
cursor: zoom-out;
}
<div class="container">
<input type="checkbox" id="zoomCheck">
<label for="zoomCheck">
<img src="https://via.placeholder.com/200">
</label>
</div>

<html>
<head>
<title>Image Zoom</title>
<style type="text/css">
#imagediv {
margin:0 auto;
height:400px;
width:400px;
overflow:hidden;
}
img {
position: relative;
left: 50%;
top: 50%;
}
</style>
</head>
<body>
<input type="button" value ="-" onclick="zoom(0.9)"/>
<input type="button" value ="+" onclick="zoom(1.1)"/>
<div id="imagediv">
<img id="pic" src=""/>
</div>
<script type="text/javascript" language="javascript">
window.onload = function(){zoom(1)}
function zoom(zm) {
img=document.getElementById("pic")
wid=img.width
ht=img.height
img.style.width=(wid*zm)+"px"
img.style.height=(ht*zm)+"px"
img.style.marginLeft = -(img.width/2) + "px";
img.style.marginTop = -(img.height/2) + "px";
}
</script>
</body>
</html>

Related

Vue add class before insert, leave it

I have a custom animation that the regular Vue transition doesn't quite cover. I have it implemented elsewhere with a conditional v-bind:class, but that doesn't work well for conditional v-if blocks or v-for groups.
I need to add a class ('open') one frame after the element is entered as with v-enter-to, but I need it to never be removed from the element.
I then need it removed removed when leaving to trigger the closing animation.
Am I using Vue Transition wrong and this is perfectly possible within transition, or is there a way to add/remove the class around the enter/leave functionality?
.accordion {
overflow: hidden;
> div {
margin-bottom: -1000px;
transition: margin-bottom .3s cubic-bezier(.5,0,.9,.8),visibility 0s .3s,max-height 0s .3s;
max-height: 0;
overflow: hidden;
}
&::after {
content: "";
height: 0;
transition: height .3s cubic-bezier(.67,.9,.76,.37);
max-height: 35px;
}
&.open {
max-height: 8000px;
> div {
transition: margin-bottom .3s cubic-bezier(.24,.98,.26,.99);
margin-bottom: 0;
max-height: 100000000px;
position: relative;
}
&::after {
height: 35px;
max-height: 0;
transition: height .3s cubic-bezier(.76,.37,.67,.9),max-height 0s .3s;
}
}
}
<transition name="accordion" :duration="300">
<div class="accordion" v-if="equipmentSelections.length === 0">
<div>
<p>Begin by selecting equipment from the list</p>
</div>
</div>
</transition>
<transition-group name="accordion" :duration="300">
<div v-for="equipment in equipmentSelections" v-bind:key="equipment.unitNumber" class="accordion">
<div>
<h3 v-on:click="updateSelections(equipment)">{{equipment.unitNumber}}</h3>
</div>
</div>
</transition-group>
You can get more power out of the vue transition component by using the javascript hooks.
For example:
Demo: https://codepen.io/KingKozo/pen/QWpBPza
HTML:
<div id="app">
<div>
<button type="button" #click="toggle">Toggle</button>
</div>
<transition name="label" v-on:enter="enter" v-on:before-leave="leave">
<div v-if="isOpen">Hi</div>
</transition>
</div>
CSS
.label-enter-active, .label-leave-active {
transition: opacity 1s;
}
.label-enter, .label-leave-to /* .fade-leave-active below version 2.1.8 */ {
opacity: 0;
}
.staying-visible {
background-color: red;
color: white;
}
Javascript
const vm = new Vue({
el: '#app',
data: {
isOpen: false
},
methods: {
enter(el){
el.classList.add("staying-visible")
},
leave(el){
el.classList.remove("staying-visible")
},
toggle(){
this.isOpen = !this.isOpen
}
}
})
In the example I provided I add a brand new class, "staying-visible", to the element on enter and remove it later on. In the example provided, I remove the class on "before-leave" so as to make the change visible but for your specific use case it seems like you can also just remove it during the 'leave' hook.
To learn more about how to use the javascript transition hooks, check out the official documentation: https://v2.vuejs.org/v2/guide/transitions.html#JavaScript-Hooks

Allow one zoomed image at a time

I am using a fairly nice zoom-in / zoom-out effect using only CSS and HTML. The issue I am having is that if a user zooms in on two images, both images will be enlarged on the page. This is technically what it should do, however I am wondering how I can disable this effect.
This is what I am looking for: e.g. there are two images on a page. If I zoom in one one image and then zoom in on the other image (while the first image is zoomed), the first image should zoom out to allow the second image to be the only zoomed image on the page. This principle would apply if there were, for example, ten images -- only one image can be zoomed at any time.
input[type=checkbox] {
display: none;
}
.container img {
width: 100%;
transition: transform 0.25s ease;
cursor: zoom-in;
}
input[type=checkbox]:checked ~ label > img {
transform: scale(2.5);
cursor: zoom-out;
}
<div class="container">
<input type="checkbox" id="zoomCheck1">
<label for="zoomCheck1">
<img src="https://www.vintagevelo.co.uk/wp-content/uploads/2018/02/DSC_0040-768x512.jpg" />
</label>
</div>
<div class="container">
<input type="checkbox" id="zoomCheck2">
<label for="zoomCheck2">
<img src="https://premium-cycling.com/wp-content/uploads/2018/05/FAGGIN-Campione-del-mondo-1980s-frameset-7.jpg" />
</label>
</div>
Use the + single next selector instead of next all
Note: I recommend using more specific class selectors of some sort, so that these styles don't end up affecting other elements unintentionally, and make the styles more reusable.
Switched to radio elements to only allow one to be checked at a time. Use "name" to group them. You can also use name attribute for the selectors.
document.querySelector('.container').addEventListener('click', e => {
if (e.target.matches('input[type=radio] + label > img')) {
e.target.parentNode.previousSibling.previousSibling.checked = !e.target.parentNode.previousSibling.previousSibling.checked;
e.preventDefault();
}
});
input[type=radio] {
display: none;
}
.container img {
width: 100%;
transition: transform 0.25s ease;
cursor: zoom-in;
}
input[type=radio]:checked+label>img {
transform: scale(2.5);
cursor: zoom-out;
}
img {
width: 50px!important;
height: 50px;
}
<div class="container">
<input type="radio" name="zooms" id="zoomCheck1">
<label for="zoomCheck1">
<img class="center round-corner"
src="https://i.imgur.com/8qmxbHT.jpeg" />
</label>
<input type="radio" name="zooms" id="zoomCheck2">
<label for="zoomCheck2">
<img class="center round-corner"
src="https://i.imgur.com/8qmxbHT.jpeg" />
</label>
</div>

CSS transition drop down - auto scroll to conditionally rendered element

I cannot find a way to properly make it so that the window automatically scrolls down to the element that is conditionally rendered and animated to drop down (and off the screen)
this is the element in question
<CSSTransitionGroup transitionEnterTimeout={1000} transitionLeaveTimeout={1000} transitionName="menu">
{
(this.state.lowerBarClicked && dropDownablePlan) ?
<div className='lowerBar__moreInfo'>
<div className='chart__header'>Usage Based Plans</div>
<div className='chart__main'>
<Chart />
</div>
<div className='chart__callToAction'>
<p className='chart-monthlyOrder'>Your Average Montly Order number: {orderNumber}</p>
<button className='btn cta'>Upgrade</button>
<p className='chart-lowerMessage'>See a feature you want? Contact us about Custom plans <a href=''>here</a></p>
</div>
</div>
: null
}
</CSSTransitionGroup>
this is the css I have for it
.menu-enter {
max-height: 0px;
-webkit-transition: max-height 1s ease;
overflow: hidden;
}
.menu-enter.menu-enter-active {
height: auto;
max-height: 40rem;
background-color: #E8E4E4;
}
.menu-leave {
max-height: 40rem;
-webkit-transition: max-height 1s ease;
}
.menu-leave.menu-leave-active {
overflow: hidden;
max-height: 0px;
}
I am not sure if this is what you mean but you can call a simple javascript command to automatically scroll down to your element
document.querySelector(".element").scrollIntoView()
in this example the element to scroll down to has the class name = element

How to add delay in css image swap?

here's a little css I use to swap images. I use them for little 32px social icons which go from green image to red image. They just swap.
This is the code (using placeholder images):
.soc img:last-child {
display: none;
}
.soc:hover img:first-child {
display: none;
}
.soc:hover img:last-child {
display: inline-block;
}
<li>
<a class="soc" href="some-link-here" target="_blank">
<img src="https://lorempixel.com/32/32/cats" />
<img src="https://lorempixel.com/32/32/food" />
</a>
</li>
I want to add a little DELAY between the swaps. Like 0.8 or 1.6 seconds.. so the transition between images / color changes are smoother and prettier.
The transition should go like this: Normal Condition to HOVER condition and also, when you just hover and move mouse away, getting back to its normal condition should also have the delay.. well that's what I would love to make.
I tried all kinds of DELAY codes that I was able to google, none worked.. But I also don't want to change the main aforementioned css code which I use, to the ones that will have to have the background-image so that for each icon I gotta make a different 5 line-long css code.
Can anyone please help me with this?
Thank you.
set the right opacity starting points
:first-child or green starts at 1 or default value and goes to 0 on :hover and :first-child or red starts at 0 and goes to 1 on :hover.
position:absolute stacks the objects on top of each other.
then use transition:opacity ease 1s for the smooth animation effect. What this property does is it tells the browsers to fade the transition from opacity:0 to opacity:1 over the duration of 1s.
adding it to the selector instead of the pseudo-class :hover makes the smooth transition occur both when you hover and when you leave the :hover state.
.soc img {
position: absolute;
width: 40px;
height: 40px;
transition: opacity ease 1s;
}
.soc img:first-child {
background: green
}
.soc:hover img:first-child {
opacity: 0
}
.soc img:last-child {
opacity: 0;
background: red
}
.soc:hover img:last-child {
opacity: 1
}
<li>
<a class="soc" href="some-link-here" target="_blank">
<img src="/iconslocation/icongreen.png" />
<img src="/iconslocation/iconred.png" /></a>
</li>
It sounds you want to soften the transition between images by fading from one to the other. I recommend using CSS to transition opacity over time.
In my example below, I've positioned the second image absolutely, so it's placed directly in front of the first one, and set it to be transparent. Then I fade it in upon hover.
ul {
list-style: none;
padding: 0;
margin: 0;
}
.soc {
position: relative;
display: inline-block;
}
.soc img:last-child {
position: absolute;
top: 0;
left: 0;
opacity: 0;
transition: opacity .2s;
}
.soc:hover img:last-child {
opacity: 1;
}
<ul>
<li>
<a class="soc" href="some-link-here" target="_blank">
<img src="//lorempixel.com/50/50/abstract/1/" />
<img src="//lorempixel.com/50/50/abstract/2/" /></a>
</li> <li>
<a class="soc" href="some-link-here" target="_blank">
<img src="//lorempixel.com/50/50/abstract/3/" />
<img src="//lorempixel.com/50/50/abstract/4/" /></a>
</li>
</ul>
Pure CSS
If we apply the default image as the background-image: url() of all a.soc elements, and give them all an :after pseudo element holding the second image to show on :hover (and optionally (and ideally for accesibility) :focus), we can then transition the opacity of the :after element to 1 on user interaction, and at full opacity it will cover/hide its parent's background-image.
The effect is as expected, but requires no <img> markup and is automatically applied by the magic of CSS to every link with class="soc".
.soc,
.soc:after {
display: inline-block;
width: 32px;
height: 32px;
background: url( https://lorempixel.com/32/32/cats );
}
.soc:after {
opacity: 0;
content: "";
position: absolute;
background: url( https://lorempixel.com/32/32/food );
transition: opacity 800ms; /* timing can be in seconds or milliseconds */
}
.soc:hover:after,
.soc:focus:after {
opacity: 1;
}
<ul>
<li><a class="soc" href="some-link-here" target="_blank"></a></li>
<li><a class="soc" href="some-other-link-here" target="_blank"></a></li>
<li><a class="soc" href="another-link-here" target="_blank"></a></li>
</ul>

How can I expand a container using :target?

I was wondering it it is possible to use a nested ":target" directive to modify elements on the page with pure CSS. I am bringing in a form text area which is absolutely positioned inside a div element (.container). When the text area appears, I want 3 things:
1) The open link to dissapear
2) The close link to appear
3) The contaner div to expand with the form element
I have been trying this by nesting the :target element inside my .container but it is not working. Is this possible?
<div class="container" id="container">
<h4>Show close</h4>
<div id="comments">
<form name="myForm">
<p>Write your comment:<br />
<textarea name="myTextarea"></textarea></p>
</form>
</div>
</div>
CSS
.container{
position:relative;
background:pink;
&:target {
transition: all 1s ease;
a#open { display: none; }
a#close {display: block;}
}
a#close { display: none; }
}
#comments {
position:absolute;
height:200px;
width:400px;
background:yellow;
left:-300%;
top:30px;
transition: all 1s ease;
}
#comments:target {
transition: all 1s ease;
left:20px;
}
JSFiddle here

Resources