Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
J
jimu-core
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
fanxuehui
jimu-core
Commits
c50ad1b9
Commit
c50ad1b9
authored
Jul 09, 2020
by
fanxuehui
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: wip
parent
e3c9c5df
Changes
16
Hide whitespace changes
Inline
Side-by-side
Showing
16 changed files
with
318 additions
and
29 deletions
+318
-29
webpack.dev.js
build/webpack.dev.js
+1
-1
extendCanvas.tsx
fixtures/extendCanvas.tsx
+2
-2
index.less
fixtures/index.less
+3
-0
index.less
fixtures/widgets/demo/index.less
+1
-2
index.ts
fixtures/widgets/demo/index.ts
+3
-0
meta.ts
fixtures/widgets/demo/meta.ts
+58
-3
meta.ts
fixtures/widgets/jiugongge/meta.ts
+10
-0
index.less
src/jimu-editor/components/canvas-host/index.less
+1
-0
index.tsx
src/jimu-editor/components/canvas-host/index.tsx
+4
-0
extended-controls.tsx
src/jimu-editor/components/extended-controls.tsx
+5
-1
index.tsx
src/jimu-editor/components/stage/index.tsx
+2
-3
index.ts
src/jimu-editor/config/index.ts
+8
-1
index.less
src/jimu-editor/hoc/layer/index.less
+1
-1
index.tsx
src/jimu-editor/hoc/layer/index.tsx
+215
-10
resizer.tsx
src/jimu-editor/hoc/layer/resizer.tsx
+3
-4
interfaces.ts
src/jimu-editor/shared/interfaces.ts
+1
-1
No files found.
build/webpack.dev.js
View file @
c50ad1b9
...
...
@@ -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
,
},
});
fixtures/extendCanvas.tsx
View file @
c50ad1b9
...
...
@@ -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
>;
...
...
fixtures/index.less
View file @
c50ad1b9
...
...
@@ -2,4 +2,7 @@
position: relative;
width: 375px;
margin: auto;
min-height: 667px;
background-color: #fff;
overflow: hidden;
}
fixtures/widgets/demo/index.less
View file @
c50ad1b9
.layer{
.layer
{
width: 100px;
height: 100px;
background-color: blue;
margin: 5px auto;
}
fixtures/widgets/demo/index.ts
View file @
c50ad1b9
...
...
@@ -3,4 +3,7 @@ import icon from './icon';
import
layer
from
'./layer'
;
import
meta
from
'./meta'
;
/*
* TODO:
*/
export
{
editor
,
icon
,
layer
,
meta
};
fixtures/widgets/demo/meta.ts
View file @
c50ad1b9
...
...
@@ -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'
},
};
fixtures/widgets/jiugongge/meta.ts
0 → 100644
View file @
c50ad1b9
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'
},
};
src/jimu-editor/components/canvas-host/index.less
View file @
c50ad1b9
.canvas_wrapper {
flex-grow: 1;
background-color: #f3f6fb;
}
src/jimu-editor/components/canvas-host/index.tsx
View file @
c50ad1b9
...
...
@@ -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
>
...
...
src/jimu-editor/components/extended-controls.tsx
View file @
c50ad1b9
...
...
@@ -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
);
src/jimu-editor/components/stage/index.tsx
View file @
c50ad1b9
...
...
@@ -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
}
...
...
src/jimu-editor/config/index.ts
View file @
c50ad1b9
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
,
};
src/jimu-editor/hoc/layer/index.less
View file @
c50ad1b9
@squareWidth:
8
px;
@squareWidth:
4
px;
@handlerWidth: 16px;
@rotateOffsetY: 50px;
@borderColor: rgb(255, 82, 82);
...
...
src/jimu-editor/hoc/layer/index.tsx
View file @
c50ad1b9
import
React
,
{
useRef
}
from
'react'
;
import
React
,
{
useRef
,
useCallback
,
useEffect
}
from
'react'
;
import
{
getNumber
}
from
'@utils/helper'
;
import
{
useStore
}
from
'@hooks/use-store'
;
import
{
Canvas
WrapperUniqueId
}
from
'@config'
;
import
{
IWidget
}
from
'@shared/interfaces'
;
import
{
Canvas
UniqueId
,
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=
{
wrappe
dWidget
.
s
tyle
}
targetStyle=
{
wrappe
rS
tyle
}
></
Resizer
>
)
:
(
''
)
}
<
wrappedWidget
.
widget
.
layer
setFocus=
{
()
=>
stageStore
.
chooseWidget
(
wrappedWidget
.
id
)
}
{
...
toJS
(
wrappedWidget
)
}
setFocus=
{
handleClick
}
{
...
clonedWrappedWidget
}
></
wrappedWidget
.
widget
.
layer
>
</
div
>
);
...
...
src/jimu-editor/hoc/layer/resizer.tsx
View file @
c50ad1b9
import
React
,
{
useCallback
,
useState
,
useEffect
,
CSSProperties
}
from
'react'
;
import
{
useSetState
}
from
'react-use'
;
import
{
getNumber
,
getDeg
}
from
'@utils/helper'
;
import
{
Canvas
Wrapper
UniqueId
}
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
(
Canvas
Wrapper
UniqueId
)
.
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
]
...
...
src/jimu-editor/shared/interfaces.ts
View file @
c50ad1b9
...
...
@@ -87,7 +87,7 @@ interface WidgetIconProps {
addSelf
:
(
attrs
:
IWidgetAttrs
)
=>
void
;
}
interface
WidgetLayerProps
{
setFocus
:
()
=>
void
;
setFocus
:
(
e
:
Event
)
=>
void
;
}
// 产出JSON格式
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment