<div id="root"></div>
body {
margin: 0;
}
const { useState, useRef, useCallback, useEffect } = React;
const BG = "https://images.ctfassets.net/w6gireivdm0c/3Kvwx5lqr71qZeY970C8yi/0e29e05c6d4fe5008c22b4017ffdfb11/cat458A8400_TP_V4.jpg";
const MAX_CANVAS_SIZE = 600;
const RANGE_SLIDER_STEP = 20;
const loadImage = (url) => {
return new Promise((resolve) => {
const img = new Image();
img.src = url;
img.crossOrigin = "Anonymous";
img.addEventListener("load", e => {
resolve(e.target);
});
});
};
const clamp = (value, min, max) => {
if (value < min) return min;
else if (value > max) return max;
return value;
};
const useDragAndDrop = () => {
const [elementPosition, setElementPosition] = useState({ top: 0, left: 0 });
const [elementOffset, setElementOffset] = useState({ x: 0, y: 0 });
const pointerStartPosition = useRef({ x: null, y: null });
const pointerMovePosition = useRef({ x: null, y: null });
const currentDragElement = useRef(null);
const prevElementOffset = useRef({ x: 0, y: 0 });
const prevElementOffsetX = useRef(0);
const prevElementOffsetY = useRef(0);
const getCurrentPosition = (elem) => {
const { top, left } = elem.getBoundingClientRect();
return { top, left };
};
const moveDistance = (distance) =>
setElementOffset({
x: prevElementOffset.current.x + distance.x,
y: prevElementOffset.current.y + distance.y
});
const resetElementOffset = () => {
setElementOffset({
x: 0,
y: 0
});
prevElementOffset.current = {
x: 0,
y: 0
};
};
const resetPointerStartPosition = () => {
if (
pointerStartPosition.current.x === null ||
pointerStartPosition.current.y === null
)
return;
pointerStartPosition.current.x = null;
pointerStartPosition.current.y = null;
};
const handleMouseDown = (e) => {
e.preventDefault();
pointerStartPosition.current.x = e.clientX;
pointerStartPosition.current.y = e.clientY;
currentDragElement.current = e.target;
const elementCurrentPosition = getCurrentPosition(currentDragElement.current);
setElementPosition({
top: elementCurrentPosition.top,
left: elementCurrentPosition.left
});
};
const handleMouseMove = (e) => {
e.preventDefault();
if (!currentDragElement.current) return;
console.log('suga move: ', currentDragElement.current)
if (
pointerStartPosition.current.x === null ||
pointerStartPosition.current.y === null
)
return;
pointerMovePosition.current.x = e.clientX;
pointerMovePosition.current.y = e.clientY;
const pointerMoveDistance = {
x: pointerMovePosition.current.x - pointerStartPosition.current.x,
y: pointerMovePosition.current.y - pointerStartPosition.current.y
};
moveDistance(pointerMoveDistance);
};
const handleMouseUp = (e) => {
e.preventDefault();
if (!currentDragElement.current) return;
console.log('suga up: ', currentDragElement.current)
resetPointerStartPosition();
const elementCurrentPosition = getCurrentPosition(currentDragElement.current);
setElementPosition({
top: elementCurrentPosition.top,
left: elementCurrentPosition.left
});
currentDragElement.current = null;
};
useEffect(() => {
document.body.addEventListener("mousemove", handleMouseMove);
document.body.addEventListener("mouseup", handleMouseUp);
document.body.addEventListener("mouseleave", handleMouseUp);
return () => {
document.body.removeEventListener("mousemove", handleMouseMove);
document.body.removeEventListener("mouseup", handleMouseUp);
document.body.removeEventListener("mouseleave", handleMouseUp);
};
}, []);
useEffect(() => {
prevElementOffset.current = {
x: elementOffset.x,
y: elementOffset.y
};
}, [elementPosition.left, elementPosition.top]);
return [
{
currentDragElement,
elementPosition,
elementOffset,
},
{
pointerStartPosition,
pointerMovePosition
},
handleMouseDown,
setElementOffset,
resetElementOffset,
setElementPosition
];
};
const useResizeObserver = (elements, callback) => {
useEffect(() => {
console.log('mari')
const resizeObserver = new ResizeObserver((entries) => callback(entries));
for (const elem of elements) {
//console.log(elem);
elem.current && resizeObserver.observe(elem.current);
}
return () => resizeObserver.disconnect();
}, []);
};
const useMutationObserver = (elements, callback, config) => {
useEffect(() => {
const mutationObserver = new MutationObserver((mutations) => {
mutationObserver.disconnect();
callback(mutations);
for (const elem of elements) {
elem.current && mutationObserver.observe(elem.current, config);
}
});
for (const elem of elements) {
elem.current && mutationObserver.observe(elem.current, config);
}
return () => mutationObserver.disconnect();
}, []);
};
const useRangeSlider = () => {
const [
element,
pointer,
handleMouseDown,
setElementOffset,
resetElementOffset,
setElementPosition
] = useDragAndDrop();
const {
currentDragElement,
elementPosition,
elementOffset
} = element
const {
pointerStartPosition,
pointerMovePosition
} = pointer
const [rangeSliderHandleOffsetX, setRangeSliderHandleOffsetX] = useState(0);
const [rangeSliderHandleWidth, setRangeSliderHandleWidth] = useState(0);
const [rangeSliderBarWidth, setRangeSliderBarWidth] = useState(0);
const [rangeSliderBarPosition, setRangeSliderBarPosition] = useState({
left: 0,
top: 0
});
const [rangeSliderHandlePosition, setRangeSliderHandlePosition] = useState(0);
const rangeSliderBarScale = useRef(1)
const rangeSliderHandleElement = useRef(null);
const rangeSliderBarElement = useRef(null);
const previousRangeSliderBarWidth = useRef(0)
const isDetectionResizeRangeSliderBar = useRef(false)
/*const handleUpdateRangeSliderBar = (mutations) => {
console.log('observe mutation bar')
if(isDetectionResizeRangeSliderBar.current) {
isDetectionResizeRangeSliderBar.current = false
//return
}
}*/
const handleUpdateRangeSliderHandlePosition = (mutations) => {
/*for (const mutation of mutations) {
if (mutation.target === rangeSliderHandleElement.current) {
const left = mutation.target.getBoundingClientRect().left;
setRangeSliderHandlePosition(left);
}
}*/
/*if(isDetectionResizeRangeSliderBar.current) {
isDetectionResizeRangeSliderBar.current = false
return
}*/
console.log('observe mutation handle')
const left = mutations[0].target.getBoundingClientRect().left;
setRangeSliderHandlePosition(left);
};
const handleResize = (entries) => {
for (const entry of entries) {
if (entry.target === rangeSliderBarElement.current) {
const width = entry.contentRect.width;
const height = entry.contentRect.height;
// バーの幅が0であれば、ハンドルが右側に位置した状態でバーの幅が0になってから再び0以上になると、ハンドルの位置がp * 0 = 0となり先頭に戻ってしまう。バーの幅が0になった後でもハンドルの現在位置が維持されるように、バーの幅か高さが0になったら前回のオフセットの状態が維持されるように抜ける
if(width === 0 || height === 0) return
// useFrontBackRatioフックだとresizeObserverの検知の速さに対応できないため、ここでスケールを取得
rangeSliderBarScale.current =
previousRangeSliderBarWidth.current === 0
? 1
: width / previousRangeSliderBarWidth.current;
setRangeSliderBarWidth(width);
previousRangeSliderBarWidth.current = width
//setRangeSliderHandleOffsetX((p) => p * rangeSliderBarScale.current)
const rect = entry.target.getBoundingClientRect();
setRangeSliderBarPosition({
left: rect.left,
top: rect.top
});
}
if (entry.target === rangeSliderHandleElement.current) {
const width = entry.contentRect.width;
setRangeSliderHandleWidth(width);
const left = entry.target.getBoundingClientRect().left;
setRangeSliderHandlePosition(left);
}
}
}
/*useResizeObserver(
[rangeSliderBarElement, rangeSliderHandleElement],
handleResize
);*/
useResizeObserver(
[rangeSliderBarElement],
(entries) => {
isDetectionResizeRangeSliderBar.current = true
console.log('observe resize bar')
const width = entries[0].contentRect.width;
const height = entries[0].contentRect.height;
// バーの幅が0であれば、ハンドルが右側に位置した状態でバーの幅が0になってから再び0以上になると、ハンドルの位置がp * 0 = 0となり先頭に戻ってしまう。バーの幅が0になった後でもハンドルの現在位置が維持されるように、バーの幅か高さが0になったら前回のオフセットの状態が維持されるように抜ける
if(width === 0 || height === 0) return
// useFrontBackRatioフックだとresizeObserverの検知の速さに対応できないため、ここでスケールを取得
rangeSliderBarScale.current =
previousRangeSliderBarWidth.current === 0
? 1
: width / previousRangeSliderBarWidth.current;
setRangeSliderBarWidth(width);
previousRangeSliderBarWidth.current = width
setRangeSliderHandleOffsetX((p) => p * rangeSliderBarScale.current)
const rect = entries[0].target.getBoundingClientRect();
setRangeSliderBarPosition({
left: rect.left,
top: rect.top
});
}
)
useResizeObserver(
[rangeSliderHandleElement],
(entries) => {
const width = entries[0].contentRect.width;
setRangeSliderHandleWidth(width);
const left = entries[0].target.getBoundingClientRect().left;
setRangeSliderHandlePosition(left);
}
);
/*useMutationObserver(
[rangeSliderBarElement],
handleUpdateRangeSliderBar,
{ attributes: true }
)*/
useMutationObserver(
[rangeSliderHandleElement],
handleUpdateRangeSliderHandlePosition,
{ attributes: true, subtree: false, childList: false, attributeFilter: ["class"] }
);
useEffect(() => {
if (!currentDragElement.current) return;
// rangeSliderBarPosition.leftだと、バーの位置が途中から変化しなくなったときに、そこから先の変更値が取得できなくなるため、押し込まれたときのバーの位置を取得する
const rect = rangeSliderBarElement.current.getBoundingClientRect()
setRangeSliderBarPosition({
left: rect.left,
top: rect.top
});
const startX = pointerStartPosition.current.x - rect.left//rangeSliderBarPosition.left;
setRangeSliderHandleOffsetX(clamp(startX, 0, rangeSliderBarWidth));
}, [elementPosition]);
useEffect(() => {
const rect = rangeSliderBarElement.current.getBoundingClientRect()
setRangeSliderBarPosition({
left: rect.left,
top: rect.top
});
const moveX = pointerMovePosition.current.x - rect.left//rangeSliderBarPosition.left;
setRangeSliderHandleOffsetX((p) => clamp(moveX, 0, rangeSliderBarWidth));
}, [elementOffset.x, elementOffset.y]);
return [
{
handle: {
element: rangeSliderHandleElement,
offsetX: rangeSliderHandleOffsetX,
position: rangeSliderHandlePosition,
width: rangeSliderHandleWidth
},
bar: {
element: rangeSliderBarElement,
position: rangeSliderBarPosition,
width: rangeSliderBarWidth,
scale: rangeSliderBarScale
},
handleMouseDown
},
setRangeSliderHandleOffsetX,
isDetectionResizeRangeSliderBar
];
};
const useIconImageGenerator = (imageUrl) => {
const [
{
currentDragElement,
elementPosition,
elementOffset
},
{
pointerStartPosition,
pointerMovePosition
},
handleMouseDown,
setElementOffset,
resetElementOffset,
setElementPosition
] = useDragAndDrop();
const [
rangeSlider,
setRangeSliderHandleOffsetX,
isDetectionResizeRangeSliderBar
] = useRangeSlider();
const { handle, bar } = rangeSlider
const {
element: rangeSliderHandleElement,
offsetX:rangeSliderHandleOffsetX,
position: rangeSliderHandlePosition,
width: rangeSliderHandleWidth
} = handle
const {
element: rangeSliderBarElement,
position: rangeSliderBarPosition,
width: rangeSliderBarWidth,
scale: rangeSliderBarScale
} = bar
const canvasElement = useRef(null);
const draggableImageElement = useRef(null);
const imageElement = useRef(null);
const dragAreaElement = useRef(null);
const frameElement = useRef(null);
const initialImageSize = useRef({ height: 0, width: 0 })
const draggableImagePosition = useRef({ top: 0, left: 0, right: 0, bottom: 0 })
const [draggableImageSize, setDraggableImageSize] = useState({ height: 0, width: 0 })
const framePosition = useRef({ left: 0, top: 0, right: 0, bottom: 0 })
const [frameSize, setFrameSize] = useState({ height: 0, width: 0 })
const frameScale = useRef(1);
const [generatedImage, setGeneratedImage] = useState(null);
const isFirstResize = useRef(false)
const isDetectionResizeFrame = useRef(false)
const fitImage = async (h, w) => {
const image = await loadImage(imageUrl).then((r) => r);
const width = image.width;
const height = image.height;
const ratio = height / width;
if (ratio >= 1) {
setDraggableImageSize({
height: height * (w / width),
width: w
})
initialImageSize.current.height = height * (w / width)
initialImageSize.current.width = w
} else {//if (ratio < 1) {
setDraggableImageSize({
height: h,
width: width * (h / height)
})
initialImageSize.current.height = h
initialImageSize.current.width = width * (h / height)
}
/*else {
setDraggableImageSize({
height: h,
width: w
})
initialImageSize.current.height = h
initialImageSize.current.width = w
}*/
}
const pushBackDraggableImage = () => {
if (draggableImagePosition.current.left >= framePosition.current.left) {
setElementOffset((p) => ({
x: p.x - Math.abs(draggableImagePosition.current.left - framePosition.current.left),
y: p.y
}));
}
if (draggableImagePosition.current.right <= framePosition.current.right) {
setElementOffset((p) => ({
x: p.x + Math.abs(draggableImagePosition.current.right - framePosition.current.right),
y: p.y
}));
}
if (draggableImagePosition.current.top >= framePosition.current.top) {
setElementOffset((p) => ({
x: p.x,
y: p.y - Math.abs(draggableImagePosition.current.top - framePosition.current.top)
}));
}
if (draggableImagePosition.current.bottom <= framePosition.current.bottom) {
setElementOffset((p) => ({
x: p.x,
y: p.y + Math.abs(draggableImagePosition.current.bottom - framePosition.current.bottom)
}));
}
}
const updateDraggableImageScale = () => {
const rangeSliderStatus = clamp(
rangeSliderHandleOffsetX / rangeSliderBarWidth,
0,
rangeSliderBarWidth
);
const imageScale = 1 + rangeSliderStatus;
setDraggableImageSize({
height: initialImageSize.current.height * imageScale,
width: initialImageSize.current.width * imageScale
})
};
const handleMouseWheel = useCallback((e) => {
if (!rangeSliderBarWidth) return;
const deltaY = e.deltaY;
if (e.deltaY > 0) {
setRangeSliderHandleOffsetX((p) =>
clamp(p - RANGE_SLIDER_STEP * rangeSliderBarScale.current, 0, rangeSliderBarWidth)
);
} else {
setRangeSliderHandleOffsetX((p) =>
clamp(p + RANGE_SLIDER_STEP * rangeSliderBarScale.current, 0, rangeSliderBarWidth)
);
}
});
const getGeneratedImage = async () => {
if (!canvasElement.current) return;
const windowHeight = window.innerHeight
const windowWidth = window.innerWidth
const windowRatio = windowHeight / windowWidth
const canvasSize = (windowRatio >= 1) ? clamp(windowWidth, 0, MAX_CANVAS_SIZE) : clamp(windowHeight, 0, MAX_CANVAS_SIZE)
canvasElement.current.width = canvasSize;
canvasElement.current.height = canvasSize;
const ctx = canvasElement.current.getContext("2d");
const canvasRect = canvasElement.current.getBoundingClientRect();
const image = await loadImage(imageElement.current.src).then((r) => r);
const frameRect = frameElement.current.getBoundingClientRect()
const imageRect = draggableImageElement.current.getBoundingClientRect()
const ratio =
initialImageSize.current.height >= initialImageSize.current.width//draggableImageSize.height > draggableImageSize.width
? canvasRect.width / frameRect.width
: canvasRect.height / frameRect.height;
ctx.drawImage(
image,
(imageRect.left - frameRect.left) * ratio,
(imageRect.top - frameRect.top) * ratio,
imageRect.width * ratio,
imageRect.height * ratio
);
if (canvasElement.current) {
const generatedImage = canvasElement.current.toDataURL("image/jpeg");
console.log('generatedImage: ', generatedImage)
//const img = new Image()
//img.src = generatedImage
//document.body.appendChild(img)
setGeneratedImage(generatedImage);
}
};
const resetIconImageGenerator = () => {
setElementOffset({
x: 0,
y: 0
});
setRangeSliderHandleOffsetX(0);
// bg選択画面に戻ったあとに再度この画面が表示されたときにimageWidth(imageHeight)がトリガーとなってgetGeneratedImageが実行されるようにするため
setDraggableImageSize({
height: 0,
width: 0
})
};
useMutationObserver(
[draggableImageElement],
(mutations) => {
console.log('naze1: ', isDetectionResizeFrame.current, isDetectionResizeRangeSliderBar.current)
// 枠のリサイズ時は画像がずれるのを防ぐため、pushBackDraggableImage関数を実行しない
// 画像がドラッグで移動したときと、レンジスライダーの操作によって画像が拡縮したときに実行する
const frameRect = frameElement.current.getBoundingClientRect()
const rect = mutations[0].target.getBoundingClientRect();
draggableImagePosition.current = {
left: rect.left,
right: rect.right,
top: rect.top,
bottom: rect.bottom
}
// リサイズされなくなったあとにドラッグしたときに画像がずれるのを防ぐために、ここでも枠の位置を更新する
framePosition.current = {
left: frameRect.left,
right: frameRect.right,
top: frameRect.top,
bottom: frameRect.bottom
}
if(isDetectionResizeRangeSliderBar.current) {
isDetectionResizeRangeSliderBar.current = false
isDetectionResizeFrame.current = false // trueのままとなり、次の判定がtrueになってしまう
return
}
//いくらリサイズされても↑でreturnし続けるので、先に進まず、isDetectionResizeFrame.currentはtrueのまま!!!その状態で、バーをクリックすると、isDetectionResizeFrame.currentがtrueなので↓でreturnとなり、クリック時はpushBackDraggableImage関数は実行されない。続けてドラッグするとそのときはfalseだから実行されるようになる
if(isDetectionResizeFrame.current) {
isDetectionResizeFrame.current = false
isDetectionResizeRangeSliderBar.current = false
return
}
console.log('mura: ', isDetectionResizeRangeSliderBar.current, isDetectionResizeFrame.current)
console.log('observe mutation image')
pushBackDraggableImage()
getGeneratedImage()
},
{
attributes: true,
subtree: false,
childList: false,
attributeFilter: ["class"]
}
)
useResizeObserver(
[frameElement],
(entries) => {
isDetectionResizeFrame.current = true
console.log('observe resize frame')
const rect = entries[0].target.getBoundingClientRect();
const resizedFrameHeight = rect.height;
const resizedFrameWidth = rect.width;
// ブラウザウィンドウのサイズが0pxになったあとに再び0より大きくなったときに画像のサイズが0pxになってしまうのを防ぐために、ブラウザウィンドウのサイズが0pxのときは処理を抜ける
if(resizedFrameHeight === 0 || resizedFrameWidth === 0) {
return
}
framePosition.current = {
left: rect.left,
right: rect.right,
top: rect.top,
bottom: rect.bottom
}
// コンポーネントのレンダリング後に一度だけ呼び出す
if(!isFirstResize.current) {
isFirstResize.current = true
fitImage(resizedFrameHeight, resizedFrameWidth)
}
console.log('aib1: ', initialImageSize.current.height, resizedFrameHeight)
const ratio = initialImageSize.current.height / initialImageSize.current.width;
console.log('aib0: ', ratio)
if (ratio >= 1) {
frameScale.current =
initialImageSize.current.width === 0//previousFrameSize.current.width === 0
? 1
: resizedFrameWidth / initialImageSize.current.width//resizedFrameWidth / previousFrameSize.current.width;
} else {
frameScale.current =
initialImageSize.current.height === 0//previousFrameSize.current.height === 0
? 1
: resizedFrameHeight / initialImageSize.current.height //resizedFrameHeight / previousFrameSize.current.height;
}
initialImageSize.current.height *= frameScale.current
initialImageSize.current.width *= frameScale.current
console.log('aib2: ', initialImageSize.current.height, resizedFrameHeight)
console.log('aib3: ', frameScale.current)
setFrameSize({
height: resizedFrameHeight,
width: resizedFrameWidth
})
setElementOffset((p) => ({
x: p.x * frameScale.current,
y: p.y * frameScale.current
}));
getGeneratedImage()
}
)
useResizeObserver(
[draggableImageElement],
(entries) => {
console.log('observe resize draggable')
/*const rect = entries[0].target.getBoundingClientRect();
draggableImagePosition.current = {
left: rect.left,
right: rect.right,
top: rect.top,
bottom: rect.bottom
}*/
/*setElementPosition({
left: draggableImagePosition.current.left,
top: draggableImagePosition.current.top
})*/
//getGeneratedImage()
}
)
/* useEffect(() => getGeneratedImage(), [
elementPosition.left,
elementPosition.top,
rangeSliderHandlePosition.left,
rangeSliderHandlePosition.top,
draggableImageSize.height,
draggableImageSize.width,
frameSize.height,
frameSize.width
]);*/
useEffect(() => {
//ページ表示時、imageWidth(imageHeight)がNaNにならないように
//if (!draggableImageSize.width) return;
//if (!draggableImageSize.height) return;
updateDraggableImageScale();
}, [rangeSliderHandleOffsetX, frameSize.height, frameSize.width]);
return [
{
dragAreaElement,
draggableImage: {
element: draggableImageElement,
size: draggableImageSize,
offset: elementOffset
},
imageElement,
frameElement,
canvasElement,
rangeSlider,
handleMouseDown,
handleMouseWheel
},
generatedImage,
resetIconImageGenerator
];
};
const StyledRangeSlider = styled.div`
display: flex;
align-items: center;
height: 6vmin;
position: relative;
width: 100%;
`;
const Bar = styled.div`
background-color: #f0f0f0;
border-radius: 2vmin;
cursor: pointer;
height: 2vmin;
position: absolute;
width: 100%;
`;
const Handle = styled.div`
background-color: #ffa44a;
border-radius: 100%;
cursor: pointer;
height: 6vmin;
position: absolute;
left: -3vmin;
transform: translate3d(
${({ offsetX }) => offsetX}px,
0,
0
);
width: 6vmin;
`;
const RangeSlider = ({ rangeSlider }) => (
<StyledRangeSlider className="range-slider">
<Bar
className="range-slider-bar"
ref={rangeSlider.bar.element}
onMouseDown={rangeSlider.handleMouseDown}
/>
<Handle
className="range-slider-handle"
ref={rangeSlider.handle.element}
onMouseDown={rangeSlider.handleMouseDown}
offsetX={rangeSlider.handle.offsetX}
/>
</StyledRangeSlider>
);
const StyledIconImageGenerator = styled.div`
margin: 3vmin auto 0;
width: 50vmin;
`;
const DragArea = styled.div`
border-radius: 2vmin;
box-sizing: border-box;
cursor: move;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
margin-bottom: 2vmin;
overflow: hidden;
padding: 2vmin;
position: relative;
height: 50vmin;
width: 100%;
`;
const DraggableImage = styled.div`
height: ${({ size }) => size.height}px;
position: absolute;
transform: translate3d(
${({ offsetX }) => offsetX}px,
${({ offsetY }) => offsetY}px,
0
);
width: ${({ size }) => size.width}px;
z-index: 0;
`;
const Frame = styled.div`
box-sizing: border-box;
box-shadow: rgba(230, 236, 240, 0.7) 0px 0px 0px 4vmin;
border: 0.6vmin solid #ff4a59;
height: 100%;
position: relative;
pointer-events: none;// これがないとドラッグしたときに画像が正しく移動しない。なぜなのかは現在検証中
width: 100%;
z-index: 1;
`;
const Img = styled.img`
bottom: 0;
height: 100%;
left: 0;
position: absolute;
right: 0;
top: 0;
width: 100%;
z-index: -1;
`;
const Canvas = styled.canvas`
background-color: red;
position: absolute;
z-index: -1000;
`;
const Flex = styled.div`
display: flex;
align-items: center;
justify-content: space-between;
`
const Margin = styled.div`
margin-top : ${({ top }) => top ? top : `0`};
margin-right : ${({ right }) => right ? right : `0`};
margin-bottom : ${({ bottom }) => bottom ? bottom : `0`};
margin-left : ${({ left }) => left ? left : `0`};
width: 100%;
`
const StyledIcon = styled.span`
color: ${({ color }) => color};
font-size: ${({ size }) => size};
`
const Icon = ({ name, color, size }) => (
<StyledIcon className="icon" color={color} size={size}>
<i class={`fas fa-${name}`}></i>
</StyledIcon>
)
const IconImageGenerator = ({
className,
imageUrl,
iconImageGenerator
}) => (
<StyledIconImageGenerator>
<DragArea
ref={iconImageGenerator.dragAreaElement}
onMouseDown={iconImageGenerator.handleMouseDown}
onWheel={iconImageGenerator.handleMouseWheel}
>
<DraggableImage
ref={iconImageGenerator.draggableImage.element}
size={iconImageGenerator.draggableImage.size}
offsetX={iconImageGenerator.draggableImage.offset.x}
offsetY={iconImageGenerator.draggableImage.offset.y}
>
<Img
src={imageUrl}
ref={iconImageGenerator.imageElement}
draggable="false"
/>
</DraggableImage>
<Frame ref={iconImageGenerator.frameElement} />
<Canvas ref={iconImageGenerator.canvasElement} draggable="false" />
</DragArea>
<Flex>
<Icon name="search-minus" color="white" size="4vmin" />
<Margin left="4vmin" right="4vmin">
<RangeSlider rangeSlider={iconImageGenerator.rangeSlider} />
</Margin>
<Icon name="search-plus" color="white" size="4vmin" />
</Flex>
</StyledIconImageGenerator>
)
const StyledApp = styled.div`
background-color: #55ab70;
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
width: 100%;
`;
const App = () => {
const [
iconImageGenerator,
generatedImage,
resetIconImageGenerator
] = useIconImageGenerator(BG);
return (
<StyledApp>
<IconImageGenerator
imageUrl={BG}
iconImageGenerator={iconImageGenerator}
/>
</StyledApp>
);
};
ReactDOM.render(<App />, document.getElementById("root"));
/*
const getGeneratedImage = async () => {
if (!canvasElement.current) return;
const windowHeight = window.innerHeight
const windowWidth = window.innerWidth
const windowRatio = windowHeight / windowWidth
const canvasSize = (windowRatio >= 1) ? clamp(windowWidth, 0, MAX_CANVAS_SIZE) : clamp(windowHeight, 0, MAX_CANVAS_SIZE)
canvasElement.current.width = canvasSize;
canvasElement.current.height = canvasSize;
const ctx = canvasElement.current.getContext("2d");
const canvasRect = canvasElement.current.getBoundingClientRect();
const image = await loadImage(imageElement.current.src).then((r) => r);
const ratio =
initialImageSize.current.height >= initialImageSize.current.width//draggableImageSize.height > draggableImageSize.width
? canvasRect.width / frameSize.width
: canvasRect.height / frameSize.height;
ctx.drawImage(
image,
(elementPosition.left - framePosition.current.left) * ratio,
(elementPosition.top - framePosition.current.top) * ratio,
draggableImageSize.width * ratio,
draggableImageSize.height * ratio
);
if (canvasElement.current) {
const generatedImage = canvasElement.current.toDataURL("image/jpeg");
console.log('generatedImage: ', generatedImage)
//const img = new Image()
//img.src = generatedImage
//document.body.appendChild(img)
setGeneratedImage(generatedImage);
}
};
*/
View Compiled
This Pen doesn't use any external CSS resources.