Commit c50ad1b9 authored by fanxuehui's avatar fanxuehui

feat: wip

parent e3c9c5df
......@@ -30,12 +30,12 @@ module.exports = merge(webpackBaseConfig, {
}),
],
devServer: {
host,
port,
inline: true,
hot: true,
disableHostCheck: true,
historyApiFallback: true, // using html5 router.
contentBase: distPath,
useLocalIp: true,
},
});
......@@ -2,14 +2,14 @@ import React from 'react';
import { toJS } from 'mobx';
import withLayer from '@hoc/layer';
import styles from './index.less';
import { CanvasUniqueId } from '@config';
/*
* todo
* 和数据流解耦
*/
function Canvas({ stageStore }) {
return (
<div className={styles.extend_canvas}>
<p>extended Canvas</p>
<div className={styles.extend_canvas} id={CanvasUniqueId}>
{stageStore.targetPage.widgetList.map((wrappedWidget) => {
const WrappedLayer = withLayer(wrappedWidget);
return <WrappedLayer key={wrappedWidget.id}></WrappedLayer>;
......
......@@ -2,4 +2,7 @@
position: relative;
width: 375px;
margin: auto;
min-height: 667px;
background-color: #fff;
overflow: hidden;
}
.layer{
.layer {
width: 100px;
height: 100px;
background-color: blue;
margin: 5px auto;
}
......@@ -3,4 +3,7 @@ import icon from './icon';
import layer from './layer';
import meta from './meta';
/*
* TODO:
*/
export { editor, icon, layer, meta };
......@@ -2,9 +2,64 @@ import { WidgetDataTypes } from '../../../src/jimu-editor';
export default {
version: '0.0.1',
deps: ['loadsh@1.1'],
script: '',
name: 'jiugongge-40e87f8e85952d6f82d8',
script:
'//yun.tuisnake.com/jimu-web/render/skin/dist/jiugongge-40e87f8e85952d6f82d8.js',
data: {
bg: { type: WidgetDataTypes.Text, value: '' },
url: {
type: WidgetDataTypes.Text,
name: '表单链接',
value:
'https://shengdianhuadg.com/frontend_service/common?appid=3&funid=27&activity_id=xym&theme=1&from_id=2&channel=86660&activity_id=xym',
},
bg: {
type: WidgetDataTypes.Image,
value: '//yun.duiba.com.cn/h5-mami/webgame/activity/jiugongge/bg02.jpg',
},
box: {
type: WidgetDataTypes.Image,
value: '//yun.duiba.com.cn/h5-mami/webgame/activity/jiugongge/box.png',
},
btn: {
type: WidgetDataTypes.Image,
value: '//yun.duiba.com.cn/h5-mami/webgame/activity/jiugongge/btn.png',
},
bottom: {
type: WidgetDataTypes.Image,
value: '//yun.duiba.com.cn/h5-mami/webgame/activity/jiugongge/bottom.png',
},
item01: {
type: WidgetDataTypes.Image,
value: '//yun.duiba.com.cn/h5-mami/webgame/activity/jiugongge/item01.png',
},
item02: {
type: WidgetDataTypes.Image,
value: '//yun.duiba.com.cn/h5-mami/webgame/activity/jiugongge/item02.png',
},
item03: {
type: WidgetDataTypes.Image,
value: '//yun.duiba.com.cn/h5-mami/webgame/activity/jiugongge/item03.png',
},
item04: {
type: WidgetDataTypes.Image,
value: '//yun.duiba.com.cn/h5-mami/webgame/activity/jiugongge/item04.png',
},
item05: {
type: WidgetDataTypes.Image,
value: '//yun.duiba.com.cn/h5-mami/webgame/activity/jiugongge/item05.png',
},
item06: {
type: WidgetDataTypes.Image,
value: '//yun.duiba.com.cn/h5-mami/webgame/activity/jiugongge/item06.png',
},
item07: {
type: WidgetDataTypes.Image,
value: '//yun.duiba.com.cn/h5-mami/webgame/activity/jiugongge/item07.png',
},
item08: {
type: WidgetDataTypes.Image,
value: '//yun.duiba.com.cn/h5-mami/webgame/activity/jiugongge/item08.png',
},
},
style: { width: '100px', height: '100px' },
style: { width: '100px', height: '100px', left: '0', top: '0' },
};
import { WidgetDataTypes } from '../../../src/jimu-editor';
export default {
version: '0.0.1',
deps: ['loadsh@1.1'],
script: '',
data: {
bg: { type: WidgetDataTypes.Text, value: '' },
},
style: { width: '100px', height: '100px', left: '0', top: '0' },
};
.canvas_wrapper {
flex-grow: 1;
background-color: #f3f6fb;
}
......@@ -9,6 +9,9 @@ import styles from './index.less';
function CanvasHost() {
const { scopeStore, stageStore } = useStore();
const { canvas } = scopeStore;
const handleClick = () => {
stageStore.chooseWidget('');
};
const [collectedProps, drop] = useDrop({
accept: ['demo'],
drop(item, monitor) {
......@@ -32,6 +35,7 @@ function CanvasHost() {
className={styles.canvas_wrapper}
ref={drop}
id={CanvasWrapperUniqueId}
onClick={handleClick}
>
<CumCanvas stageStore={stageStore}></CumCanvas>
</div>
......
......@@ -4,7 +4,11 @@ import { useStore } from '../hooks/use-store';
import { observer } from 'mobx-react';
function ExtendedControls() {
const { scopeStore } = useStore();
return <div style={{ position: 'absolute', top: 30 }}>ExtendedControls</div>;
return (
<div style={{ position: 'absolute', top: 30, left: 230 }}>
ExtendedControls
</div>
);
}
export default observer(ExtendedControls);
......@@ -10,9 +10,9 @@ import CardHost from '../card-host';
function Stage() {
const { scopeStore, stageStore } = useStore();
const { canvas } = scopeStore;
const handleClick = () => {};
function handleMouseMove(e) {
e.preventDefault();
// e.preventDefault();
typeof window.__widgetMouseMove__ === 'function' &&
window.__widgetMouseMove__(e.clientX, e.clientY);
typeof window.__widgetResizerMouseMove__ === 'function' &&
......@@ -28,7 +28,6 @@ function Stage() {
}
return (
<div
onClick={handleClick}
className={styles.stage}
onMouseMoveCapture={handleMouseMove}
onMouseUp={handleMouseUp}
......
export const CanvasWrapperUniqueId = 'jimu____canvas____id';
export const CanvasWrapperUniqueId = 'jimu____canvas____wrapper____id';
export const CanvasUniqueId = 'jimu____canvas____id';
export const draggingInScene = {
// 舞台中拖动水平磁吸效果参数
HorizontalDeviation: 10,
// 舞台中拖动垂直磁吸效果参数
VerticalDeviation: 10,
};
@squareWidth: 8px;
@squareWidth: 4px;
@handlerWidth: 16px;
@rotateOffsetY: 50px;
@borderColor: rgb(255, 82, 82);
......
import React, { useRef } from 'react';
import React, { useRef, useCallback, useEffect } from 'react';
import { getNumber } from '@utils/helper';
import { useStore } from '@hooks/use-store';
import { CanvasWrapperUniqueId } from '@config';
import { IWidget } from '@shared/interfaces';
import { CanvasUniqueId, draggingInScene } from '@config';
import { IWidget, IWrappedWidget } from '@shared/interfaces';
import { toJS } from 'mobx';
import Resizer from './resizer';
import { observer } from 'mobx-react';
import styles from './index.less';
function withLayer(wrappedWidget) {
import { pick, unset, clone } from 'lodash-es';
import { useSetState } from 'react-use';
function withLayer(wrappedWidget: IWrappedWidget) {
function Wrapper() {
const { scopeStore, stageStore } = useStore();
const wrapperRef = useRef<HTMLDivElement>();
const mover = {
left: true,
top: true,
};
const [state, setState] = useSetState({
mouseStartPos: {
x: NaN,
y: NaN,
},
targetStartPos: {
x: NaN,
y: NaN,
},
targetPos: {
left: NaN,
top: NaN,
},
_target: {
width: NaN,
height: NaN,
transform: '',
opacity: NaN,
zIndex: NaN,
},
// 这里借用React做Dom渲染
isDraggingInScene: false,
indicator: {},
// hover效果
hover: false,
});
const handleClick = (e) => {
stageStore.chooseWidget(wrappedWidget.id);
};
const handleReszie = ({ width, height, left, top, deg }) => {
const widgetDom = document.querySelector<HTMLDivElement>(
`[wid="${wrappedWidget.id}"`
);
wrapperRef.current.style.left = left + 'px';
wrapperRef.current.style.top = top + 'px';
wrapperRef.current.style.width = width + 'px';
......@@ -31,24 +63,197 @@ function withLayer(wrappedWidget) {
'style.transform': `rotate(${deg}deg)`,
});
};
const handleMouseDown = (e) => {
handleClick(e);
e.stopPropagation();
// 初始化拖拽信息
const widgetDom = wrapperRef.current;
setState({
mouseStartPos: {
x: e.clientX,
y: e.clientY,
},
targetStartPos: {
x: getNumber(widgetDom.style.left),
y: getNumber(widgetDom.style.top),
},
});
// 修改组件拖动状态为true(优化后)
if (!state.isDraggingInScene) {
setState({
isDraggingInScene: true,
});
}
};
const __handleMouseMove__ = useCallback(
(x, y) => {
// !更新该组件位置(调用频次很高,注意性能)
// console.time('计算拖动所需时间')
const widgetDom = wrapperRef.current;
const left = state.targetStartPos.x + (x - state.mouseStartPos.x);
const top = state.targetStartPos.y + (y - state.mouseStartPos.y);
// 水平偏差量
const HDeviation = draggingInScene.HorizontalDeviation;
// 场景当前宽
const sceneWidth = getNumber(
window.getComputedStyle(document.getElementById(CanvasUniqueId)).width
);
// widget属性
const widgetWidth = getNumber(widgetDom.style.width);
const widgetLeft = left;
// 距离舞台中心线偏移距离
const offsetDis = sceneWidth / 2 - (widgetWidth / 2 + widgetLeft);
// 是否几乎居中
const isAlmostHorizontalCenter = Math.abs(offsetDis) < HDeviation;
// 是否几乎居左
const isAlmostHorizontalLeft = Math.abs(widgetLeft) < HDeviation;
// 是否几乎居右
const isAlmostHorizontalRight =
Math.abs(sceneWidth - widgetWidth - widgetLeft) < HDeviation;
// 居中此div实际需要的left值
const centerLeft = (sceneWidth - widgetWidth) / 2;
// 居左此div实际需要的left值
const startLeft = 0;
// 居右此div实际需要的left值
const endLeft = centerLeft * 2;
// 修改实际dom left值
if (mover.left) {
widgetDom.style.left =
(isAlmostHorizontalCenter
? centerLeft
: isAlmostHorizontalLeft
? startLeft
: isAlmostHorizontalRight
? endLeft
: widgetLeft) + 'px';
}
// 垂直偏差量
const VDeviation = draggingInScene.VerticalDeviation;
// 场景当前高
const sceneHeight = getNumber(
window.getComputedStyle(document.getElementById(CanvasUniqueId))
.height
);
// widget属性
const widgetHeight = getNumber(widgetDom.style.height);
const widgetTop = top;
// 距离舞台中心偏移距离
const offsetDisY = sceneHeight / 2 - (widgetHeight / 2 + widgetTop);
// 是否几乎居中
const isAlmostVerticalCenter = Math.abs(offsetDisY) < VDeviation;
// 是否几乎居顶
const isAlmostVerticalTop = Math.abs(widgetTop) < VDeviation;
// 是否几乎居底
const isAlmostVerticalBottom =
Math.abs(sceneHeight - widgetHeight - widgetTop) < VDeviation;
// 居中此div实际需要的top值
const centerTop = (sceneHeight - widgetHeight) / 2;
// 居顶此div实际需要的top值
const startTop = 0;
// 居底此div实际需要的top值
const endTop = centerTop * 2;
// 修改实际dom top值
if (mover.top) {
widgetDom.style.top =
(isAlmostVerticalCenter
? centerTop
: isAlmostVerticalTop
? startTop
: isAlmostVerticalBottom
? endTop
: widgetTop) + 'px';
}
/*
* TODO:重写吸附策略
*/
},
[state]
);
const __handleMouseUp__ = (e) => {
const dpr = 1;
const widgetDom = wrapperRef.current;
const left = dpr * getNumber(widgetDom.style.left) + 'px';
const top = dpr * getNumber(widgetDom.style.top) + 'px';
let updated = {};
if (mover.top) updated['style.top'] = top;
if (mover.left) updated['style.left'] = left;
// 更新store
stageStore.changeTargetProps(updated);
// 修改组件拖动状态为false
setState({
isDraggingInScene: false,
});
};
useEffect(() => {
if (state.isDraggingInScene) {
// 使能mousemove与mouseup
window.__widgetMouseMove__ = __handleMouseMove__;
window.__widgetMouseUp__ = __handleMouseUp__;
} else {
// 删除mousemove与mouseup,释放内存
window.__widgetMouseMove__ = null;
window.__widgetMouseUp__ = null;
}
}, [__handleMouseMove__, __handleMouseUp__, state]);
const clonedWrappedWidget = clone(toJS(wrappedWidget));
const { style } = clonedWrappedWidget;
/*
* todo
* 1. 过滤样式暴露给组件定制
* 2. 支持流式布局
*/
const wrapperStyle = pick(style, [
'left',
'right',
'top',
'width',
'height',
'transform',
'zIndex',
'display',
'bottom',
]);
// 给定width,height,position使得Inner好布局
clonedWrappedWidget.style.width = '100%';
clonedWrappedWidget.style.height = '100%';
clonedWrappedWidget.style.position = 'relative';
// 卸载掉实际widget组件不能也不应修改的属性
unset(clonedWrappedWidget.style, 'left');
unset(clonedWrappedWidget.style, 'top');
unset(clonedWrappedWidget.style, 'transform');
unset(clonedWrappedWidget.style, 'zIndex');
unset(clonedWrappedWidget.style, 'right');
unset(clonedWrappedWidget.style, 'bottom');
return (
<div
className={styles.widget_layer_wrapper}
style={wrapperStyle}
id={wrappedWidget.id}
ref={wrapperRef}
onMouseDown={handleMouseDown}
onClick={(e) => e.stopPropagation()}
>
{stageStore.targetWidgetId === wrappedWidget.id ? (
<Resizer
onReszie={handleReszie}
onReszieComplete={hanldeReszieComplete}
targetStyle={wrappedWidget.style}
targetStyle={wrapperStyle}
></Resizer>
) : (
''
)}
<wrappedWidget.widget.layer
setFocus={() => stageStore.chooseWidget(wrappedWidget.id)}
{...toJS(wrappedWidget)}
setFocus={handleClick}
{...clonedWrappedWidget}
></wrappedWidget.widget.layer>
</div>
);
......
import React, { useCallback, useState, useEffect, CSSProperties } from 'react';
import { useSetState } from 'react-use';
import { getNumber, getDeg } from '@utils/helper';
import { CanvasWrapperUniqueId } from '@config';
import { CanvasUniqueId } from '@config';
import styles from './index.less';
type IState = {
mouseInitPos: {
......@@ -38,9 +38,9 @@ function Resizer(props) {
deg,
};
} else if (type === 'ro') {
const { left, top, width, height } = state.initTarget;
const { left = '0px', top = '0px', width, height } = state.initTarget;
const sceneInnerRect = document
.getElementById(CanvasWrapperUniqueId)
.getElementById(CanvasUniqueId)
.getBoundingClientRect();
const deg = getDeg(
sceneInnerRect.left + getNumber(left) + getNumber(width) / 2,
......@@ -161,7 +161,6 @@ function Resizer(props) {
e.persist();
e.stopPropagation();
const result = calc(state.type, e);
console.log(result, state.type, e);
props.onReszie(result);
},
[state]
......
......@@ -87,7 +87,7 @@ interface WidgetIconProps {
addSelf: (attrs: IWidgetAttrs) => void;
}
interface WidgetLayerProps {
setFocus: () => void;
setFocus: (e: Event) => void;
}
// 产出JSON格式
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment