I have spent many days trying to make an item resizable that is rotated with interact.js.
This is the code that I have at this moment, I will try to explain the concept.
We have a selector item for two reasons, because the container could be scaled with css transform (like a zoom), and we need to have the selector outside and because we have a multiselection, and the selector grow if I have two rectangle selected, but in this case this is not the main problem and we have calculated the scaled proportion without problems and other things.
When the selector is resize, it take the rectangle, and make the same with the width, height, left, top and rotation.
Javascript:
// TAP - CLICK EVENT (just for positioning the selector)
interact('#rectangle').on('tap', event => {
console.log('Tap Box!');
event.stopPropagation();
const $rectangleCloned = $('#rectangle').clone();
const previousTransform = $rectangleCloned.css('transform');
$rectangleCloned.css('transform', 'none');
$rectangleCloned.css('opacity', '0');
$rectangleCloned.css('display', 'block');
$('#container').append($rectangleCloned);
const values = $rectangleCloned[0].getBoundingClientRect();
// This is just a trick for fast implementation:
$('#selector').css('top', values.y);
$('#selector').css('left', values.x);
$('#selector').css('width', values.width);
$('#selector').css('height', values.height);
$('#selector').css('transform', previousTransform);
$rectangleCloned.remove();
return values;
});
interact('.pointer9').draggable({
max: 1,
onmove: event => {
const angleDeg =
Math.atan2(
centerRotate.posY - event.pageY,
centerRotate.posX - event.pageX
) *
180 /
Math.PI;
console.log(this.rotate);
const prevAngle = this.rotate - angleInitial;
const angle = parseInt(angleDeg) + prevAngle;
this.$rectangle.css({
transform: 'rotate(' + angle + 'deg)'
});
this.$selector.css({
transform: 'rotate(' + angle + 'deg)'
});
},
onstart: event => {
const data = event.interactable.getRect(event.target.parentNode);
this.centerRotate = {
posX: data.left + data.width / 2,
posY: data.top + data.height / 2
};
this.angleInitial =
Math.atan2(
centerRotate.posY - event.pageY,
centerRotate.posX - event.pageX
) *
180 /
Math.PI;
this.$rectangle = $('#rectangle');
this.$selector = $('#selector');
this.rotate = $rectangle.attr('angle') || 0;
},
onend: event => {
const $box = $('#selector');
const matrix = $box.css('transform');
const values = matrix
.split('(')[1]
.split(')')[0]
.split(',');
var a = values[0];
var b = values[1];
var angle = Math.round(Math.atan2(b, a) * (180 / Math.PI));
$rectangle.attr('angle', angle);
}
});
interact('#selector')
.resizable({
// resize from all edges and corners
edges: {
left: true,
right: true,
bottom: true,
top: true
},
// keep the edges inside the parent
restrictEdges: {
outer: 'parent',
endOnly: true,
},
// minimum size
restrictSize: {
min: {
width: 100,
height: 50
},
},
inertia: true,
})
.on('resizemove', function(event) {
var target = event.target,
x = parseFloat($(target).offset().left) || 0,
y = parseFloat($(target).offset().top) || 0;
// update the element's style
target.style.width = event.rect.width + 'px';
target.style.height = event.rect.height + 'px';
// translate when resizing from top or left edges
x += event.deltaRect.left;
y += event.deltaRect.top;
target.style.left = x + 'px';
target.style.top = y + 'px';
$('#rectangle')[0].style.left = target.style.left;
$('#rectangle')[0].style.top = target.style.top;
$('#rectangle')[0].style.width = target.style.width;
$('#rectangle')[0].style.height = target.style.height;
target.setAttribute('data-x', x);
target.setAttribute('data-y', y);
});
CSS:
#container {
width: 500px;
height: 400px;
top: 0;
left: 0;
position: absolute;
background-color: #CCC;
}
#rectangle {
top: 50px;
left: 50px;
width: 120px;
height: 60px;
background-color: red;
position: absolute;
}
#selector {
display: inline-block;
position: absolute;
pointer-events: none;
z-index: 9999;
top: -1000px;
/*Not showing at start*/
}
#selector .pointers {
display: inline-block;
position: absolute;
z-index: 2;
width: 10px;
height: 10px;
pointer-events: all;
}
#selector .pointers .point {
width: 10px;
height: 10px;
background-color: #fff;
border: 2px solid rgba(0, 0, 0, 0.9);
border-radius: 50%;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
#selector .pointers.pointer1 {
top: -5px;
left: -5px;
}
#selector .pointers.pointer2 {
bottom: -5px;
left: -5px;
}
#selector .pointers.pointer3 {
top: -5px;
right: -5px;
}
#selector .pointers.pointer4 {
bottom: -5px;
right: -5px;
}
#selector .pointers.pointer-north {
top: -5px;
left: calc(50% - 5px);
}
#selector .pointers.pointer-south {
bottom: -5px;
left: calc(50% - 5px);
}
#selector .pointers.pointer-east {
right: -5px;
top: calc(50% - 5px);
}
#selector .pointers.pointer-west {
left: -5px;
top: calc(50% - 5px);
}
#selector .pointer-rotate {
border: 2px solid rgba(0, 0, 0, 0.9);
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
border-radius: 50%;
cursor: rotate;
}
#selector .pointer9 {
bottom: -70px;
left: calc(50% - 11px);
display: inline-block;
width: 20px;
height: 20px;
background-color: #fff;
pointer-events: all;
position: absolute;
}
#selector .rotate-line {
border-left: 1px dashed #5f5f5f;
height: 40px;
position: absolute;
top: -40px;
left: calc(50% - 1px);
width: 1px;
}
HTML:
<div id="container">
<div id="rectangle">
</div>
<div id="selector">
<div class="pointers pointer1">
<div class="point"></div>
</div>
<div class="pointers pointer2">
<div class="point">
</div>
</div>
<div class="pointers pointer3">
<div class="point">
</div>
</div>
<div class="pointers pointer4">
<div class="point">
</div>
</div>
<div class="pointers pointer-north">
<div class="point">
</div>
</div>
<div class="pointers pointer-east">
<div class="point">
</div>
</div>
<div class="pointers pointer-south">
<div class="point">
</div>
</div>
<div class="pointers pointer-west">
<div class="point">
</div>
</div>
<span class="topline lines-resize" />
<span class="rightline lines-resize" />
<span class="botline lines-resize" />
<span class="leftline lines-resize" />
<div class="pointer-rotate pointer9" />
<div class="rotate-line" />
</div>
</div>
Fiddle for testing:
https://jsfiddle.net/ub70028c/46/
I have read about other people trying to make the same without not results...
Thanks!
I checked your code and a similar library for resizable and rotatable and I figure out your problem.
First, checking similar library:
Please see this fiddle that I created by jquery.freetrans.js.
If you inspect on <div class="shape">, you can see
transform: matrix(1, 0, 0, 1, 0, 0);
If you rotate it, transform changed like below:
transform: matrix(0.997373, -0.0724379, 0.0724379, 0.997373, 0, 0);
In similar case, your code uses transform that at first, it doesn't transform and after rotating, it has like below:
transform: rotate(-2.49576deg);
If you can use matrix instead of rotate in transform, your code will work properly. If you can't change it, you can use similar library like jquery.freetrans.jsthat work properly with rotate and resize together.
https://github.com/taye/interact.js/issues/569
https://github.com/taye/interact.js/issues/499
https://github.com/taye/interact.js/issues/394
I am afraid you have chosen a library whose author has clearly stated his intent
There's no built-in way. As I mentioned in #137 I'm not really interested in handling scaled or rotated elements
So the question you should ask yourself is
Do I want to find a workaround to make this library work or choose a different library perhaps?
Update-1: 28-Apr-2018
In case you want to do it in canvas instead of normal elements then I found fabric.js a good option
we are very close to finish the work after five days... we need to optimice all the mathematical calculations... but yes, this is what I was looking for:
Sorry, but we don't have the code ready... I will post all with comments for other people.
Comments: For a mathematician, this task is not very complex because all the angles are rectangular (90º). I will try to make a PR to the Interact.js, even to other libraries to implement this feature by default. Hope this work help to other developers ;)
Related
I'm making an app using JavaScript and JQuery, which will tell the user if there device is straight or not, basically like a spirit level. I want to draw a line a straight line across the middle of the screen and i want this to be responsive no matter the size of the device. This will be used on mobiles and tablets. I used a canvas to the draw a line and so far i'm not sure if this is the right way to approach this?
if anyone could give me any advice i would really appreciate it. Below is my canvas line so far. And I've included some rough drawing of what i mean.
const c = document.getElementById("LineCanvas");
const drw = c.getContext("2d");
drw.beginPath();
drw.moveTo(10,45);
drw.lineTo(180,47);
drw.lineWidth = 5;
drw.strokeStyle = '#006400';
drw.stroke();
If the phone is aligned straight the line will be green else red
to draw the line you can use a pseudo element from HTML or body or any specific tag that you want to use in a specific page or click , then update rotation via transform:rotate() ; or rotate3D()
example ( without javascript, rotate values will have to be taken from your device via your app ):
let level = document.querySelector("#level");
document.querySelector("#spirit").onclick = function() {
level.classList.toggle('show');
}
#level {
width: 100vw;
height: 100vh;
position: fixed;
top: 0;
left: 0;
display: none;
pointer-events: none;
}
#level.show {
display: block;
}
#level::before {
content: '';
position: absolute;
width: 200vmax;
margin: 0 -50vmax;
border-top: 1px solid;
box-shadow: 0 0 1px 5px #bee;
top: 50%;
transform: rotate(0deg);
}
#level.show~#spirit::before {
content: 'Hide';
}
#level:not(.show)~#spirit::before {
content: 'Show';
}
/* animation to fake phone device moving */
#level::before {
animation: rt 10s infinite;
}
#keyframes rt {
20% {
transform: rotate3d(1, -1, 1, -0.25turn);
}
40% {
transform: rotate3d(1, 1, 1, 0.5turn);
}
60% {
transform: rotate3d(1, -1, 1, -0.75turn);
}
80% {
transform: rotate3d(1, 1, -1, -0.5turn);
}
}
<div id="level">
<!-- to show on a single page or via js on user request -->
</div>
<button id="spirit" type=button> that spirit level</button>
While drawing a line with canvas can work you might find it more straightforward to draw it with a simple div element. When you sense a slope you can change its color to red and back to green if it's level.
Of course you will have to do some calculations to decide what angle you want the line to be - but I guess that is the whole point of your webapp to show people how far off they are.
When you know the angle you want the line to be call slope(n) where n is the number of degrees. I've also put in a simple button so the user can choose whether to show the line or not but I expect you'll have your own code for that.
On any page where you want the user to be able to show the line put this in the head:
<style>
* {
margin: 0;
padding: 0;
}
.linecontainer {
position: absolute;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
overflow: hidden;
z-index: 99999;
}
#line {
width: 200vmax;
height: 5px;
position: relative;
top: 50%;
left: calc(50vw - 100vmax);
transform: rotate(45deg);
background-color:red;
}
.hideline {
display: none;
}
#showbtn {
font-size: 20px;
background-color: blue;
color: white;
height: 2em;
width: auto;
padding: 2px;
}
</style>
and put this in the main body of the page:
<div class="linecontainer">
<div id="showbtn" onclick="document.getElementById('line').classList.toggle('hideline');">
Click me to show/hide the line
</div>
<div id="line"></div>
</div>
<script>
function slope(deg) {
let line = document.getElementById('line');
line.style.backgroundColor = ( deg%180 == 0 ) ? 'green' : 'red';
line.style.transform = 'rotate(' + deg + 'deg)';
}
</script>
Here's a snippet where you can show the line at different angles.
function slope(deg) {
let line = document.getElementById('line');
line.style.backgroundColor = ( deg%180 == 0 ) ? 'green' : 'red';
line.style.transform = 'rotate(' + deg + 'deg)';
}
* {
margin: 0;
padding: 0;
}
.linecontainer {
position: absolute;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
overflow: hidden;
z-index: 99999;
}
#line {
width: 200vmax;
height: 5px;
position: relative;
top: 50%;
left: calc(50vw - 100vmax);
transform: rotate(45deg);
background-color:red;
}
.hideline {
display: none;
}
#showbtn {
font-size: 20px;
background-color: blue;
color: white;
height: 2em;
width: auto;
padding: 2px;
}
<div class="linecontainer">
<div id="showbtn" onclick="document.getElementById('line').classList.toggle('hideline');">
Click me to show/hide the line
</div>
<div id="line"></div>
</div>
<!-- this is just for the demo -->
<div style="background-#eeeeee;font-size: 20px;position:fixed;z-index:100000;bottom:0;left:0;">How many degrees do you want me to rotate? <input style="font-size:20px;"value="45" onchange="slope(this.value);"/></div>
For my portfolio website, I want to include info text that becomes visible when hovering over the according image and I want the text to follow along the cursor.
I'm by no means a coding expert, so I tried to achieve the effect by replacing the default cursor with an image of the text on white background via css and the cursor-property.
However, this left me with weird gray edged around the image that the image originally doesn't have.
So I figured that this was a sloppy approach anyway and that I should rather try solving it via javascript... which left me with the following code:
$(document).bind('mousemove', function(e){
$('#tail').css({
left: e.clientX + 20,
top: e.clientY + document.body.scrollTop
});
});
#tail {
position: absolute;
background-color: #ffffff;
padding: 5px;
opacity: 0;
}
#tail p {
margin: 0px;
}
.project-01:hover > #tail {
opacity: 100%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="project-01">
<a href="project-site-01.html">
<img src="images/project-cover-01.png" alt="Project description">
</a>
<div id="tail">
<p>Project description</p>
</div>
</div>
I am now left with text that appears when hovering over the image and it follows the cursor properly, even if the cursor position changes due to scrolling (which it didn't do properly at first, which is why I added the 'document.body.scrollTop').
The only problem: The info text is way to far away from the cursor. I tried adjusting the offset, adding '- 900' after 'document.body.scrollTop' but that only makes it look right with my specific browser height – if I switch to a smaller or bigger screen, the '- 900' of course doesn't fit anymore.
Is there anyone who can explain what I'm doing wrong on a dummy level or even better – tell me how to fix the problem? I've been trying to get that hover text effect working for literally the past two days. HELP!
PS: You can see the effect I want to create on https://playgroundparis.com
I hope this can help you!
Edit: Technically this is a duplicated. I realized the problem with scrolling that you talking about. I've found a solution in this post and I readaptated it for
your specific case.
var mouseX = 0, mouseY = 0, limitX, limitY, containerWidth;
window.onload = function(e) {
var containerObjStyle = window.getComputedStyle(document.querySelectorAll(".project-01")[0]);
containerWidth = parseFloat(containerObjStyle.width).toFixed(0);
containerHeight = parseFloat(containerObjStyle.height).toFixed(0);
var follower = document.querySelector('#tail');
var xp = 0, yp = 0;
limitX = containerWidth;
limitY = containerHeight;
var loop = setInterval(function(){
//Change the value 5 in both axis to set the distance between cursor and text.
xp = (mouseX == limitX) ? limitX : mouseX + 5;
xp = (xp < 0) ? 0 : xp;
yp = (mouseY == limitY) ? limitY : mouseY + 5;
yp = (yp < 0) ? 0 : yp;
follower.style.left = xp + 'px';
follower.style.top = yp + 'px';
}, 15);
window.onresize = function(e) {
limitX = parseFloat(window.getComputedStyle(document.querySelectorAll(".project-01")[0]).width).toFixed(0);
}
document.onmousemove = function(e) {
mouseX = Math.min(e.pageX, limitX);
mouseY = Math.min(e.pageY, limitY);
}
};
//Change the 100 value to set the fade time (ms).
$(".project-01").hover(function () {
$(this).find('#tail').fadeIn(100);
},
function () {
$(this).find('#tail').fadeOut(100);
});
#tail {
position: absolute;
background-color: #ffffff;
padding: 5px;
overflow: hidden;
}
#debug {
position: absolute;
right: 0;
top: 100px;
width: 100px;
height:100px;
background-color: red;
color: black;
}
#tail p {
margin: 0px;
}
.project-01 {
width: 300px;
overflow: hidden;
}
.project-01 img {
height: 100%;
width: 100%;
}
.project-01 a {
height: 100%;
width: 100%;
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="project-01">
<a href="project-site-01.html">
<img src="https://picsum.photos/200/300" alt="Project description">
</a>
<div id="tail">
<p>Project descriptions</p>
</div>
</div>
You can use the below code's
.description {
display: none;
position: absolute;
z-index: 2000 !important;
color: black;
padding: 15px;
margin-left: 32px;
margin-top: -200px;
top: auto;
height: auto;
width: 500px;
}
.image {
height: 200px;
width: 200px;
}
.my-image:hover + .description {
display: block;
position: absolute;
background-color: black;
color: white;
}
.description:hover {
display: block;
background-color: black;
color: white;
}
<div class="project-01">
<a href="project-site-01.html" class="my-image">
<img src="https://homepages.cae.wisc.edu/~ece533/images/monarch.png" alt="Project description" class="image">
</a>
<div id="tail" class="description">
Butterflies are insects in the macrolepidopteran clade Rhopalocera from the order Lepidoptera, which also includes moths. Adult butterflies have large, often brightly coloured wings, and conspicuous, fluttering flight.
</div>
</div>
I hope this helps i recenty made one myselff for my website a few days ago
No info cursor:
.info:hover .tooltip {
color: red;
visibility: visible;
opacity: 1;
transition: opacity 1s
}
.tooltip {
visibility: hidden;
opacity: 0;
transition: opacity 1s
}
}
.tootip:hover {
visibility: visible
}
<span class="info"><img src="https://google.com/favicon.ico">Hover Me</img> <span class="tooltip">Welcome</span></a></span>
With info cursor:
.info:hover .tooltip {
color: red;
visibility: visible;
opacity: 1;
transition: opacity 1s
}
.tooltip {
visibility: hidden;
opacity: 0;
transition: opacity 1s
}
}
.tootip:hover {
visibility: visible
}
.info {
cursor: help
}
<span class="info"><img src="https://google.com/favicon.ico">Hover Me</img> <span class="tooltip">Welcome</span></a></span>
I have created a div inside a container that points to the cursor. The issue is that the accuracy is off and I need to change the angle.
HTML
<div class="shooting_container">
<div class="shooter">
<div class="shooting_arm">
</div>
</div>
</div>
CSS
.shooter {
height: 480px;
width: 200px;
background-color: white;
bottom: 150px;
margin: 0 100px;
position: absolute;
.shooting_arm {
height: 245px;
width: 166px;
bottom: 500px;
position: absolute;
top: calc(50% - 40px);
transform: translateY(-50%) rotate(-12deg);
z-index: 500;
transform-origin: 156px 8px;
left: -43px;
background-color: red;
// Where the transform origin lies
&::after {
position: absolute;
top: 8px;
left: 156px;
width: 5px;
height: 5px;
content: '';
background-color: #f0f;
border-radius: 50%;
transform: translate(-50%, -50%);
}
}
}
JS
$(document).on('mousemove', moveCursor);
function moveCursor(e) {
var box = $(".shooter .shooting_arm");
var boxCenter = [box.offset().left+box.width()/2, box.offset().top+box.height()/2];
var angle = Math.atan2(e.pageX - boxCenter[0], - (e.pageY - boxCenter[1]) )*(180/Math.PI) - 180;
if (angle < -160) {
angle = -0;
}
setArm(box, angle);
}
function setArm(arm, angle) {
arm.css({ "-webkit-transform": 'translateY(-50%) rotate(' + angle + 'deg)'});
arm.css({ '-moz-transform': 'translateY(-50%) rotate(' + angle + 'deg)'});
}
CodePen:
https://codepen.io/anon/pen/vrWJwZ
Problem 1: Angle is incorrect and differs as you move
Incorrect angle. I need the top of the container to align with the cursor, not the center. I tried adjusting:
var boxCenter = [box.offset().left+box.width()/2, box.offset().top+box.height()/2];
to
var boxCenter = [box.offset().left+box.width()/2, box.offset().top+box.height()/2/2];
But it still was not in line with the top of the container.
Problem 2: Glitches when cursor is within the div itself
For some reason it returns two different angle values when the cursor is within the container and causes it to jiggle. Why does it do this?
Trying to move a position:fixed div on scroll by changing the top: css value in javascript. The div won't move though, not sure why.
html:
<div id="red">
<div id="blue"></div>
</div>
css:
#red {
position: relative;
float: left;
width: 100%;
height: 1000px;
background: rgba(255, 0, 0, 0.2);
border: solid 2px #f0f;
}
#blue {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 50vh;
background: rgba(0, 0, 255, 0.2);
border: solid 2px #0ff;
}
js:
window.addEventListener('scroll', function() {
var yPos = -(Math.floor(document.body.scrollTop / 10));
//console.log("yPos = " + yPos); //output is correct
document.getElementById('blue').style.top = yPos + 'px';
//document.getElementById('blue').setAttribute('top',yPos); //also tried this
});
https://jsfiddle.net/akzx43yL/
Why isn't the top css value changing and how can I get it to do so? No jquery please.
Two things:
Instead of document.documentElement.scrollTop, you should use window.pageYOffset (scrollTop doesn't play nicely in Chrome).
You need to add a unit of measurement after you update top; values other than 0 should have px appened to them.
This can be seen in the following:
window.addEventListener('scroll', function() {
var yPos = -(Math.floor(window.pageYOffset / 10));
document.getElementById('blue').style.top = yPos + "px";
// Optionally log the `top` value
//console.log(document.getElementById('blue').style.top);
});
#red {
position: relative;
float: left;
width: 100%;
height: 1000px;
background: rgba(255, 0, 0, 0.2);
border: solid 2px #f0f;
}
#blue {
position: absolute;
top: -10px;
left: 0;
width: 100vw;
height: 50vh;
background: rgba(0, 0, 255, 0.2);
border: solid 2px #0ff;
}
<div id="red">
<div id="blue"></div>
</div>
Hope this helps! :)
If you check your console, you will be see your console.log("yPos = " + yPos) is always 0 you most update your code as follow:
window.addEventListener('scroll', function() {
var yPos = -(Math.floor(document.documentElement.scrollTop / 10));
console.log("yPos = " + yPos);
document.getElementById('blue').style.top = yPos + "px";
});
Tip:
Ways to get srollTop (pure js):
var top = window.pageYOffset || document.documentElement.scrollTop;
This is what worked for me:
document.getElementById('blue').style.top = yPos + "px";
I have a panel on the left side of my screen that flips out when activated. As you move your mouse around, it slightly alters the rotateY transform for a nice interactive effect. I want to mirror this on the right side of the screen, but every adjustment I make just causes the panel to freak out when you move the mouse.
What adjustments need to be made to the second jquery function to mirror the effect? I tried a lot of things including the current code which is replacing x = x + 15 with x = 360 - (x + 15). It's close, but still not right.
$(document).on('mousemove','#viewport1 .menu',function( event ) {
var x = Math.round(event.pageX / $(this).width() * 10);
x = x + 15;
$(this).css('transform','rotateY(' + x + 'deg)');
});
$(document).on('mousemove','#viewport2 .menu',function( event ) {
var x = Math.round(event.pageX / $(this).width() * 10);
x = 360 - (x + 15); //this is almost but not quite right...
$(this).css('transform','rotateY(' + x + 'deg)');
});
.viewport {
perspective: 1000px;
position: absolute;
top: 0; bottom:0; width: 30%;
padding: 5px;
}
.menu {
text-align: center;
width: 100%;
border: 1px solid black;
display: inline-block;
height: 100%;
}
#viewport1 {
left: 0;
}
#viewport1 .menu {
perspective-origin: left;
transform-origin: left;
transform: rotateY(15deg);
}
#viewport2 {
text-align: right;
right: 0;
}
#viewport2 .menu {
perspective-origin: right;
transform-origin: right;
transform: rotateY(345deg);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="viewport1" class="viewport">
<div class="menu">HOVER ME!</div>
</div>
<div id="viewport2" class="viewport">
<div class="menu">HOVER ME!</div>
</div>
You are using event.pageX, which is the position of the mouse pointer, relative to the left edge of the document, to calculate the rotation of the <div>. You need to substract the left offset: $(this).offset().left. After that you change x+15 to x-15 and you get the mirrored effect.
$(document).on('mousemove','#viewport1 .menu',function( event ) {
var x = Math.round(event.pageX / $(this).width() * 10);
x = x + 15;
$(this).css('transform','rotateY(' + x + 'deg)');
});
$(document).on('mousemove','#viewport2 .menu',function( event ) {
var x = Math.round((event.pageX - $(this).offset().left) / $(this).width() * 10);
x = x - 15;
$(this).css('transform','rotateY(' + x + 'deg)');
});
.viewport {
perspective: 1000px;
position: absolute;
top: 0; bottom:0; width: 30%;
padding: 5px;
}
.menu {
width: 100%;
border: 1px solid black;
display: inline-block;
height: 100%;
}
#viewport1 {
left: 0;
}
#viewport1 .menu {
perspective-origin: left;
transform-origin: left;
transform: rotateY(15deg);
}
#viewport2 {
text-align: right;
right: 0;
}
#viewport2 .menu {
perspective-origin: right;
transform-origin: right;
transform: rotateY(345deg);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="viewport1" class="viewport">
<div class="menu"></div>
</div>
<div id="viewport2" class="viewport">
<div class="menu"></div>
</div>