1

I'm working on an application where the user can choose photo frame for the uploaded picture with KonvaJS and React. I'm using KonvaJS for drawing the frame around the picture. The image of a frame's material is scaled to fit the frame while preserving ratio of the image. (as shown in the picture below).

Selected frame

What is the problem: The left and the right side of the frame look like the image has either been scaled too big or too small and this happens only in Safari. I checked all the calculations done for scaling the pattern image multiple times and yet the frame looks like below in Safari. The frame behaves correctly in Chrome and Firefox.

Faulty behavior of the frame

What am I trying to achieve here: Fix the scaling of the pattern image, so the left and the right side of the frame would fit correctly while preserving the image ratio.

What I currently use:

  • react-konva 16.13.0-6
  • Konva 7.1.3
  • on desktop: Safari 14.0.1
  • on mobile: Safari 12.4.8

What I have already tried:

  • Tried to change the calculations done for scaling the pattern image.
  • Checked all the values of the frame's measurements on multiple browsers.
  • Tried to draw the image on external canvas with manual scaling on that canvas. Then used it to fill as a pattern image. https://github.com/konvajs/konva/issues/1007

frameUtils.js:

// flat array of points coordinates [x1, y1, x2, y2, x3, y3]
export const topPoints = (frameWidth, topPadding, sidePadding) => [
    0,
    0,
    frameWidth,
    0,
    frameWidth - sidePadding,
    topPadding,
    sidePadding,
    topPadding,
    0,
    0
];

export const leftPoints = (frameHeight, sidePadding, topPadding, bottomPadding) => [
    0,
    0,
    0,
    frameHeight,
    sidePadding,
    frameHeight - bottomPadding,
    sidePadding,
    topPadding,
    sidePadding,
    topPadding,
    0,
    0,
    0
];

export const bottomPoints = (frameHeight, frameWidth, bottomPadding, sidePadding) => [
    0,
    frameHeight,
    sidePadding,
    frameHeight - bottomPadding,
    frameWidth - sidePadding,
    frameHeight - bottomPadding,
    frameWidth,
    frameHeight,
    0,
    frameHeight
];

export const rightPoints = (frameWidth, frameHeight, sidePadding, topPadding, bottomPadding) => [
    frameWidth,
    0,
    frameWidth,
    frameHeight,
    frameWidth - sidePadding,
    frameHeight - bottomPadding,
    frameWidth - sidePadding,
    topPadding,
    frameWidth,
    0
];

Frame.jsx:

import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import {Line, Group} from 'react-konva';
import {topPoints, leftPoints, bottomPoints, rightPoints, loadPicture} from "./frameUtils";

const Frame = ({
    frameWidth,
    frameHeight,
    padding,
    frameTexture,
    coordinates = {x: 0, y: 0},
    onChange,
    onClick,
    frameRef,
    scaleX,
    scaleY,
    patternScaleX,
    patternScaleY
}) => {
    const [frameImg, setFrameImg] = useState(null);
    const [loadedFrame, setLoadedFrame] = useState(null);

    useEffect(() => {
        if (frameTexture && frameTexture.image && loadedFrame !== frameTexture.image) {
            setLoadedFrame(frameTexture.image);
            loadPicture(frameTexture.image)
                .then(setFrameImg);
        }

    }, [frameTexture]);

    // flat array of points coordinates [x1, y1, x2, y2, x3, y3]
    const topFrameSide = topPoints(frameWidth, padding, padding);

    const leftFrameSide = leftPoints(frameHeight, padding, padding, padding);

    const bottomFrameSide = bottomPoints(frameHeight, frameWidth, padding, padding);

    const rightFrameSide = rightPoints(frameWidth, frameHeight, padding, padding, padding);

    return (
        <Group x={coordinates.x} y={coordinates.y} onChange={onChange} onClick={onClick} ref={frameRef} scaleX={scaleX} scaleY={scaleY}>
            <Line points={topFrameSide} fillPatternImage={frameImg} fillPatternRepeat={'repeat-x'} fillPatternScale={{x: patternScaleX, y: patternScaleY}} fillPatternRotation={0} closed={true}/>
            <Line points={leftFrameSide} fillPatternImage={frameImg} fillPatternRepeat={'repeat-x'} fillPatternScale={{x: patternScaleX, y: patternScaleY}} fillPatternRotation={270} closed={true}/>
            <Line points={bottomFrameSide} fillPatternImage={frameImg} fillPatternRepeat={'repeat-x'} fillPatternScale={{x: patternScaleX, y: patternScaleY}} fillPatternY={frameHeight} fillPatternRotation={180} closed={true}/>
            <Line points={rightFrameSide} fillPatternImage={frameImg} fillPatternRepeat={'repeat-x'} fillPatternScale={{x: patternScaleX, y: patternScaleY}} fillPatternX={frameWidth} fillPatternRotation={90} closed={true}/>
        </Group>
    )
};

Frame.propTypes = {
    frameWidth: PropTypes.number.isRequired,
    frameHeight: PropTypes.number.isRequired,
    padding: PropTypes.number.isRequired,
    frameTexture: PropTypes.object,
    coordinates: PropTypes.object,
    onChange: PropTypes.func,
    onClick: PropTypes.func,
    frameRef: PropTypes.any,
    scaleX: PropTypes.number,
    scaleY: PropTypes.number,
    patternScaleX: PropTypes.number,
    patternScaleY: PropTypes.number
};

export default Frame;

I think I may have overlooked some important detail or did just something wrong to cause this bug. What could cause this to happen?

Edit 12/15/2020 part 1: removed some unnecessary lines of code, will add small demo later.

Edit 12/15/2020 part 2: added a small online demo with only relevant part of the code. The problem can be clearly seen if opened in both Chrome and Safari: https://codesandbox.io/s/sleepy-ellis-6n5bw

Testy
  • 71
  • 1
  • 7
  • Can you make a small online demo? Do you have issues on mobile or desktop safari? Or both? – lavrton Dec 14 '20 at 19:30
  • I'll try to make a small online demo as soon as I can. Just tested on iPhone 6 safari with iOS 12.4.8 and I had also found an issue with it, along with desktop safari iOS 14.0.1. – Testy Dec 15 '20 at 09:53
  • 1
    @lavrton I added a small online demo, hopefully it helps. – Testy Dec 16 '20 at 05:05

0 Answers0