diff --git a/assets/icon.png b/assets/icon.png index 1c2db1c..29d46ea 100644 Binary files a/assets/icon.png and b/assets/icon.png differ diff --git a/assets/icon.pptx b/assets/icon.pptx new file mode 100644 index 0000000..efe6a33 Binary files /dev/null and b/assets/icon.pptx differ diff --git a/capabilities.json b/capabilities.json index c93e05b..e6ae99f 100644 --- a/capabilities.json +++ b/capabilities.json @@ -1,64 +1,84 @@ { "dataRoles": [{ "displayName": "urls", + "displayNameKey": "roles-urls-displayName", "name": "urls", "kind": "Grouping", - "description": "Urls of pictures, example: 'http://www.example.com/xx.png'" + "description": "Urls of pictures, example: 'http://www.example.com/xx.png'", + "descriptionKey": "roles-urls-description" }], "objects": { "layout": { "displayName": "layout", + "displayNameKey": "objects-layout-displayName", "properties": { "rowsCount": { "type": { "numeric": true }, "displayName": "rowsCount", - "description": "How many rows?" + "displayNameKey": "objects-layout-rowsCount-displayName", + "description": "How many rows?", + "descriptionKey": "objects-layout-rowsCount-descriptionKey" }, "rowGap": { "type": { "numeric": true }, "placeHolderText": "{value}%", - "displayName": "rowGap", - "description": "The distance moved during each cycle.(%)" + "displayName": "rowGap(%)", + "displayNameKey": "objects-layout-rowGap-displayName", + "description": "The distance moved during each cycle.(%)", + "descriptionKey": "objects-layout-rowGap-descriptionKey" + }, "columnGap": { "type": { "numeric": true }, "placeHolderText": "msec", - "displayName": "columnGap", - "description": "How long a cycle takes? (msec)" + "displayName": "columnGap(%)", + "displayNameKey": "objects-layout-columnGap-displayName", + "description": "How long a cycle takes? (msec)", + "descriptionKey": "objects-layout-columnGap-descriptionKey" } - } }, "animation": { "displayName": "animation", + "displayNameKey": "objects-animation-displayName", "properties": { - "feet": { + "stepLength": { "type": { "numeric": true }, - "placeHolderText": "{value}%", - "displayName": "feet", - "description": "The distance moved during each cycle.(%)" + "displayName": "stepLength(%)", + "displayNameKey": "objects-animation-stepLength-displayName", + "description": "The distance moved during each cycle.(%)", + "descriptionKey": "objects-animation-stepLength-descriptionKey" + }, "interval": { "type": { "numeric": true }, "placeHolderText": "numeric", - "displayName": "interval", - "description": "How long a cycle takes? (msec)" - } + "displayName": "interval(msec)", + "displayNameKey": "objects-animation-interval-displayName", + "description": "How long a cycle takes? (msec)", + "descriptionKey": "objects-animation-interval-descriptionKey" + } } } }, "dataViewMappings": [{ + "conditions": [{ + "urls": { + "min": 1, + "max": 1 + } + }], "categorical": { "categories": { "for": { diff --git a/src/css/picsScroller.css b/src/css/picsScroller.css index 95c1fca..282340d 100644 --- a/src/css/picsScroller.css +++ b/src/css/picsScroller.css @@ -5,12 +5,12 @@ div.pics-sroller-inner-container { display: flex; flex-direction: column; overflow: hidden; + margin: 0px; } div.pics-scroller-row-container { width: 100%; - overflow-x: hidden; - overflow-y: nowrap; + overflow: hidden; white-space: nowrap; display: flex; flex-direction: row; diff --git a/src/settings.ts b/src/settings.ts index b5ce74b..09fc6bb 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -35,7 +35,7 @@ export class VisualSettings extends DataViewObjectsParser { } export class AnimationSettings { - public feet:number=1;//n% * width + public stepLength:number=1;//n% * width public interval:number=20;//ms } export class LayoutSettings{ diff --git a/src/visual.ts b/src/visual.ts index 7bd169a..f3481ed 100644 --- a/src/visual.ts +++ b/src/visual.ts @@ -38,7 +38,7 @@ import DataView = powerbi.DataView; import VisualObjectInstanceEnumerationObject = powerbi.VisualObjectInstanceEnumerationObject; import { VisualSettings } from "./settings"; -import { timeout } from "d3"; +import * as d3 from "d3"; export interface IPicsScrollerData { @@ -47,10 +47,22 @@ export interface IPicsScrollerData { export class Visual implements IVisual { private container: HTMLElement; private settings: VisualSettings; + private host:powerbi.extensibility.visual.IVisualHost; + private selectionManager: powerbi.extensibility.ISelectionManager; private innerContainer!: HTMLDivElement; private animationPlaying: boolean = true;//控制动画暂停 constructor(options: VisualConstructorOptions) { this.container = options.element; + this.host=options.host; + this.selectionManager=this.host.createSelectionManager(); + d3.select(this.container).on("contextmenu", () => { + const mouseEvent: MouseEvent = d3.event; + this.selectionManager.showContextMenu({}, { + x: mouseEvent.clientX, + y: mouseEvent.clientY + }); + mouseEvent.preventDefault(); + }); } private initial() { if (this.innerContainer) { @@ -61,16 +73,29 @@ export class Visual implements IVisual { this.container.appendChild(this.innerContainer); } public update(options: VisualUpdateOptions) { - this.settings = Visual.parseSettings(options && options.dataViews && options.dataViews[0]); + let settings = Visual.parseSettings(options && options.dataViews && options.dataViews[0]); + this.settings=settings; this.initial(); + //处理输入 let data: IPicsScrollerData = { urls: options.dataViews[0].categorical.categories[0].values }; - let feet = options.viewport.width / 100 * this.settings.animation.feet; + //feet + settings.animation.stepLength=(settings.animation.stepLength>=0&&settings.animation.stepLength<=100)?settings.animation.stepLength:1; + let feet = options.viewport.width / 100 * settings.animation.stepLength; + //interval + settings.animation.interval=settings.animation.interval>0?settings.animation.interval:100; let interval = this.settings.animation.interval; - let rowGap = options.viewport.height / 100 * this.settings.layout.rowGap; + //rowGap + settings.layout.rowGap=(settings.layout.rowGap>=0&&settings.layout.rowGap<=100)?settings.layout.rowGap:0; + let rowGap = options.viewport.height / 100 * settings.layout.rowGap; + //columnGap + settings.layout.columnGap=(settings.layout.columnGap>=0&&settings.layout.columnGap<=100)?settings.layout.columnGap:0; let columnGap = options.viewport.width / 100 * this.settings.layout.columnGap; + + //rowsCount + settings.layout.rowsCount=settings.layout.rowsCount>=1?Math.floor(settings.layout.rowsCount):1; let rowsCount=this.settings.layout.rowsCount; - //处理输入 + if (!(data.urls.length > 0) || !(rowsCount! > 0)) { console.info("picsScroller error: (source_url.length,rowsCount) ", data.urls.length + "_" + rowsCount); return; @@ -91,14 +116,15 @@ export class Visual implements IVisual { let rowContainer = document.createElement('div'); this.innerContainer.appendChild(rowContainer); rowContainer.classList.add('pics-scroller-row-container'); - if (rowNum > 0) { - rowContainer.style.marginTop = rowGap + "px"; - }; + rowContainer.setAttribute('id', 'pics-scroller-row-container' + rowNum); rowContainer.setAttribute("style", "height:" + 100 / rowsCount + "%;"); rowContainer.setAttribute("data-animation-playing", 'false'); rowContainer.setAttribute("data-display-placehold", "false"); - + //Add rowGap + if (rowNum > 0) { + rowContainer.style.marginTop = rowGap + "px"; + }; //双倍图片容器,第一组用来显示,第二组放上去暂时隐藏 //如果第一组发生了溢出,就显示出第二组并应用滚动动画 for (let i = 0; i < 2; i++) { diff --git a/stringResources/zh-CN.json b/stringResources/zh-CN.json new file mode 100644 index 0000000..7cf0a72 --- /dev/null +++ b/stringResources/zh-CN.json @@ -0,0 +1,7 @@ +{ + "locale": "zh-CN", + "values": { + "roles-urls-displayName": "图片url", + "roles-urls-description": "图片的url,例如:'http://www.example.com/xx.png'" + } +} \ No newline at end of file diff --git a/stringResources/zh-CN/resources.resjson b/stringResources/zh-CN/resources.resjson new file mode 100644 index 0000000..fdf0cb6 --- /dev/null +++ b/stringResources/zh-CN/resources.resjson @@ -0,0 +1,17 @@ +{ + "roles-urls-displayName": "图片url", + "roles-urls-description": "图片的url,例如:'http://www.example.com/xx.png'", + "objects-layout-displayName": "布局", + "objects-layout-rowsCount-displayName": "行数", + "objects-layout-rowsCount-descriptionKey": "需要排列为几行?", + "objects-layout-rowGap-displayName": "行间距", + "objects-layout-rowGap-descriptionKey": "按视觉对象高度的百分比(%)", + "objects-layout-columnGap-displayName": "列间距", + "objects-layout-columnGap-descriptionKey": "按视觉对象宽度的百分比(%)", + "objects-animation-displayName": "动画", + "objects-animation-stepLength-displayName": "步长(%)", + "objects-animation-stepLength-descriptionKey": "每个周期内滚动的长度占视觉对象宽度的百分比(%)", + "objects-animation-interval-displayName": "周期(毫秒)", + "objects-animation-interval-descriptionKey": "滚动的间隔,单位(毫秒)" + +} \ No newline at end of file