I have a div
wrapping a canvas
. I want to be able to resize the div
. Im using resize: "both"
and overflow: "auto"
on the wrapping div
. This works fine on Chrome, but bizarrely, on Safari, if I write anything to the canvas, I now CANNOT resize the div
. The cursor also no longer changes when I hover over the resize animation in the bottom right.
The code is:
function Base() {
React.useEffect(() => {
const canvas = document.getElementById("covering-canvas-11");
const context = canvas.getContext("2d");
console.log("context for convercaning canvas 11 is ", context);
if (context) {
context.clearRect(0, 0, 40, 40);
context.fillStyle = "white";
}
});
return (
<div>
<div
style={{
resize: "both",
overflow: "auto",
background: "#ccc",
width: "40px",
height: "40px",
}}
>
<canvas id={`covering-canvas-11`} width="40px" height="40px"></canvas>
</div>
</div>
);
}
ReactDOM.render(
<Base />,
document.getElementById('root')
);
The CodePen is here: https://codepen.io/mark-kelly-the-looper/pen/gOjwjyz
This works on Chrome, I can hover over the div
and expand it. But not on Safari. When I comment out the useEffect
so there is no longer any writing to the canvas
and check on Safari - now the div
can be resized!
EDIT: CodePen with drawing to the canvas (div is NOT resizable on Safari): https://codepen.io/mark-kelly-the-looper/pen/rNrMELR
CodePen without drawing to canvas (div IS resizable in Safari): https://codepen.io/mark-kelly-the-looper/pen/rNrMELR
EDIT:
Im using Safari 14.0.3. If others cannot reproduce this issue, please comment.
EDIT My app allows users to draw shapes on the canvas, so canvas listens mouse move, mouse down, mouse up events.
It's a bug and it's reproducible.
To fix it in Safari, add this to your canvas
styles.
position: relative;
z-index: -1;
And this to the container div
.
position: relative;
z-index: 0;
Modify the values for position
and the z-index
based on your real needs. But keep the combination and a negative value for z-index
of the canvas
, and 0
or more for the container div
.
So targeting Safari only, your code becomes:
function Base() {
React.useEffect(() => {
const canvas = document.getElementById("covering-canvas-11");
const context = canvas.getContext("2d");
console.log("context for convercaning canvas 11 is ", context);
if (context) {
context.clearRect(0, 0, 40, 40);
context.fillStyle = "9ff";
context.fillRect(0, 0, 40, 40);
}
});
function handleCanvasClick(e) {
e.preventDefault();
console.log('You clicked the canvas.');
}
const canvasSafariCSS = `
/* CSS targeting Safari only */
@media not all and (min-resolution:.001dpcm) {
.canvas-container {
position: relative;
z-index: 0;
}
.canvas-container canvas {
position: relative;
z-index: -1;
}
}
`;
return (
<div>
<style>{canvasSafariCSS}</style>
<div
className = "canvas-container"
style={{
resize: "both",
overflow: "auto",
background: "#ccc",
width: "40px",
height: "40px",
}}
>
<canvas id={`covering-canvas-11`} width="40px" height="40px"
onClick = {handleCanvasClick}
></canvas>
</div>
</div>
);
}
ReactDOM.render(
<Base />,
document.getElementById('root')
);
The gist of this method is to establish a stacking context (and this) for the container div
and keep the position
and a negative z-index
for the canvas
So in essence, for the container div
, aside from
position: relative;
z-index: 0;
Something like the following will work too:
transform: translateX(0);
or an opacity
less than 1:
opacity: 0.99;
or even:
will-change: opacity;
e.g.
function Base() {
React.useEffect(() => {
const canvas = document.getElementById("covering-canvas-11");
const context = canvas.getContext("2d");
console.log("context for convercaning canvas 11 is ", context);
if (context) {
context.clearRect(0, 0, 40, 40);
context.fillStyle = "9ff";
context.fillRect(0, 0, 40, 40);
}
});
function handleCanvasClick(e) {
e.preventDefault();
console.log('You clicked the canvas.');
}
const canvasSafariCSS = `
/* CSS targeting Safari only */
@media not all and (min-resolution:.001dpcm) {
.canvas-container {
will-change: opacity;
}
.canvas-container canvas {
position: relative;
z-index: -1;
}
}
`;
return (
<div>
<style>{canvasSafariCSS}</style>
<div
className = "canvas-container"
style={{
resize: "both",
overflow: "auto",
background: "#ccc",
width: "40px",
height: "40px",
}}
>
<canvas id={`covering-canvas-11`} width="40px" height="40px"
onClick = {handleCanvasClick}
></canvas>
</div>
</div>
);
}
ReactDOM.render(
<Base />,
document.getElementById('root')
);