The outer element is interpolated between scaleY(1)
and scaleY(0.5)
.
At time t
, the transformation will be scaleY(1-t/2)
t = 0 t t = 1s
┌ ┐ ┌ ┐ ┌ ┐
│ 1 0 0 0 │ │ 1 0 0 0 │ │ 1 0 0 0 │
│ 0 1 0 0 │ │ 0 1-t/2 0 0 │ │ 0 1/2 0 0 │
│ 0 0 1 0 │ │ 0 0 1 0 │ │ 0 0 1 0 │
│ 0 0 0 1 │ │ 0 0 0 1 │ │ 0 0 0 1 │
└ ┘ └ ┘ └ ┘
The inner element is interpolated between scaleY(1)
and scaleY(2)
.
At time t
, the transformation will be scaleY(1+t)
t = 0 t t = 1s
┌ ┐ ┌ ┐ ┌ ┐
│ 1 0 0 0 │ │ 1 0 0 0 │ │ 1 0 0 0 │
│ 0 1 0 0 │ │ 0 1+t 0 0 │ │ 0 2 0 0 │
│ 0 0 1 0 │ │ 0 0 1 0 │ │ 0 0 1 0 │
│ 0 0 0 1 │ │ 0 0 0 1 │ │ 0 0 0 1 │
└ ┘ └ ┘ └ ┘
However, that's relatively to the outer. In absolute terms, the matrices are multiplied:
t = 0 t t = 1s
┌ ┐ ┌ ┐ ┌ ┐
│ 1 0 0 0 │ │ 1 0 0 0 │ │ 1 0 0 0 │
│ 0 1*1 0 0 │ │ 0 1+t/2-t²/2 0 0 │ │ 0 2/2 0 0 │
│ 0 0 1 0 │ │ 0 0 1 0 │ │ 0 0 1 0 │
│ 0 0 0 1 │ │ 0 0 0 1 │ │ 0 0 0 1 │
└ ┘ └ ┘ └ ┘
Then, yes, the start and end points correspond to the identity matrix.
But in between you have the parabola scaleY(1+t/2-t²/2)
.

It might be possible to achieve the desired effect with bezier curves.
Let f(t)
and g(t)
be the timing functions of .outer
and .inner
, respectively.
By definition, f(0) = g(0) = 0
and f(1) = g(1) = 1
.
The scale of the outer will be given by
( 1-f(t)/2 ) ( 1+g(t) ) = 1 + g(t) - f(t)/2 - f(t)g(t)/2
We want that to be 1
, so
f(t) = 2 g(t) / (1+g(t))
f'(t) = 2 g'(t) / (1+g(t))^2
f'(0) = 2 g'(0)
f'(1) = g'(1) / 2
That is, the starting slope of the outer must be the double than the inner, and viceversa for the ending slopes.
Choosing f(t) = t
(linear) and g
the bezier curve given by (.3, 0.15), (.7, .4)
seems to produce good results. Note g'(0) = 2 = 2 f'(0)
and g'(1) = 1/2 = 1/2 f'(0)
.
.outer, .inner, .inner-expectation {
transition: transform 2s;
height: 400px;
}
.outer, .inner {
transition-timing-function: linear;
}
.inner {
transition-timing-function: cubic-bezier(.3, 0.15, .7, .4);
}
.inner {
background-image: url(https://upload.wikimedia.org/wikipedia/commons/thumb/f/f4/Souq_Waqif%2C_Doha%2C_Catar%2C_2013-08-05%2C_DD_107.JPG/1920px-Souq_Waqif%2C_Doha%2C_Catar%2C_2013-08-05%2C_DD_107.JPG);
background-size: cover;
}
a:hover ~ .outer {
transform: scaleY(0.5);
}
a:hover ~ .outer .inner {
transform: scaleY(2);
}
* {
box-sizing: border-box;
}
a {
display: block;
position: absolute;
top: 0;
left: 200px;
padding: 20px;
background: wheat;
}
.text {
position: absolute;
top: 0;
height: 100%;
}
.outer {
background: #fcf8b3;
position: absolute;
top: 80px;
left: 200px;
width: 400px;
height: 400px;
}
.inner, .inner-expectation {
position: absolute;
width: 300px;
top: 0;
left: 100px;
}
.inner .text, .inner-expectation .text {
right: 0;
}
.inner-expectation {
width: 20px;
top: 80px;
left: 610px;
background: rgba(255, 0, 0, 0.5);
}
<a href="#">hover me</a>
<div class="outer">
<div class="text">outer</div>
<div class="inner">
<div class="text">inner</div>
</div>
</div>
<div class="inner-expectation"></div>
The problem is when hover ends, then the effect breaks. But this can't be solved perfectly.
When reversing, the scale of the outer will be (1+f(t))/2
, and the scale of the inner 2-g(t)
(relatively to the outer).
In absolute terms, the scale of the inner will be
(1+f(t))/2 * (2-g(t)) = 1 - g(t)/2 + f(t) - f(t)g(t)/2
And we want that to be 1
. Then, f(t) = g(t) / ( 2-g(t) )
.
But we already had f(t) = 2 g(t) / (1+g(t))
.
g(t) / ( 2-g(t) ) = 2 g(t) / (1+g(t)) => g(t) = 1
But g(0)
must be 0
. Contradiction. You can't achieve a perfect result in both directions.
If you are willing to use JS, you could swap the timing functions of the outer and the inner when the mouse enters or leaves the target element.